Skip to content

Commit

Permalink
refactor: adapt AddressBookTestingTool to support transaction Bytes (#…
Browse files Browse the repository at this point in the history
…17197)

Signed-off-by: Ivan Kavaldzhiev <[email protected]>
  • Loading branch information
IvanKavaldzhiev authored Jan 13, 2025
1 parent 8c9930a commit f26286a
Show file tree
Hide file tree
Showing 4 changed files with 433 additions and 10 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,25 @@
// SPDX-License-Identifier: Apache-2.0
/*
* Copyright (C) 2025 Hedera Hashgraph, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

plugins { id("org.hiero.gradle.module.application") }

application.mainClass = "com.swirlds.demo.addressbook.AddressBookTestingToolMain"

testModuleInfo {
requires("org.assertj.core")
requires("org.junit.jupiter.api")
requires("org.mockito")
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
import static com.swirlds.platform.test.fixtures.state.FakeStateLifecycles.FAKE_MERKLE_STATE_LIFECYCLES;
import static com.swirlds.platform.test.fixtures.state.FakeStateLifecycles.registerMerkleStateRootClassIds;

import com.hedera.hapi.platform.event.StateSignatureTransaction;
import com.hedera.pbj.runtime.io.buffer.Bytes;
import com.swirlds.common.constructable.ClassConstructorPair;
import com.swirlds.common.constructable.ConstructableRegistry;
import com.swirlds.common.constructable.ConstructableRegistryException;
Expand Down Expand Up @@ -163,4 +165,9 @@ public BasicSoftwareVersion getSoftwareVersion() {
logger.info(STARTUP.getMarker(), "returning software version {}", softwareVersion);
return softwareVersion;
}

@Override
public Bytes encodeSystemTransaction(@NonNull final StateSignatureTransaction transaction) {
return StateSignatureTransaction.PROTOBUF.toBytes(transaction);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,9 @@
import com.swirlds.platform.system.address.Address;
import com.swirlds.platform.system.address.AddressBook;
import com.swirlds.platform.system.address.AddressBookUtils;
import com.swirlds.platform.system.events.ConsensusEvent;
import com.swirlds.platform.system.events.Event;
import com.swirlds.platform.system.transaction.ConsensusTransaction;
import com.swirlds.platform.system.transaction.Transaction;
import com.swirlds.state.merkle.singleton.StringLeaf;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
Expand All @@ -70,7 +71,6 @@
import java.nio.file.Path;
import java.text.ParseException;
import java.time.Duration;
import java.util.Iterator;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
Expand Down Expand Up @@ -214,6 +214,29 @@ public void init(
logger.info(STARTUP.getMarker(), "Registered PlatformService and RosterService states.");
}

@Override
public void preHandle(
@NonNull final Event event,
@NonNull final Consumer<ScopedSystemTransaction<StateSignatureTransaction>> stateSignatureTransaction) {
event.transactionIterator().forEachRemaining(transaction -> {
// We are not interested in pre-handling any system transactions, as they are
// specific for the platform only.We also don't want to consume deprecated
// EventTransaction.STATE_SIGNATURE_TRANSACTION system transactions in the
// callback,since it's intended to be used only for the new form of encoded system
// transactions in Bytes. Thus, we can directly skip the current
// iteration, if it processes a deprecated system transaction with the
// EventTransaction.STATE_SIGNATURE_TRANSACTION type.
if (transaction.isSystem()) {
return;
}

// We should consume in the callback the new form of system transactions in Bytes
if (areTransactionBytesSystemOnes(transaction)) {
consumeSystemTransaction(transaction, event, stateSignatureTransaction);
}
});
}

/**
* {@inheritDoc}
*/
Expand All @@ -238,11 +261,26 @@ public void handleConsensusRound(
roundsHandled++;
setChild(ROUND_HANDLED_INDEX, new StringLeaf(Long.toString(roundsHandled)));

final Iterator<ConsensusEvent> eventIterator = round.iterator();
for (final var event : round) {
event.consensusTransactionIterator().forEachRemaining(transaction -> {
// We are not interested in handling any system transactions, as they are
// specific for the platform only.We also don't want to consume deprecated
// EventTransaction.STATE_SIGNATURE_TRANSACTION system transactions in the
// callback, since it's intended to be used only for the new form of encoded system
// transactions in Bytes. Thus, we can directly skip the current
// iteration, if it processes a deprecated system transaction with the
// EventTransaction.STATE_SIGNATURE_TRANSACTION type.
if (transaction.isSystem()) {
return;
}

while (eventIterator.hasNext()) {
final ConsensusEvent event = eventIterator.next();
event.consensusTransactionIterator().forEachRemaining(this::handleTransaction);
// We should consume in the callback the new form of system transactions in Bytes
if (areTransactionBytesSystemOnes(transaction)) {
consumeSystemTransaction(transaction, event, stateSignatureTransaction);
} else {
handleTransaction(transaction);
}
});
}

if (!validationPerformed.getAndSet(true)) {
Expand All @@ -255,15 +293,44 @@ public void handleConsensusRound(
}
}

/**
* Checks if the transaction bytes are system ones. The test creates application transactions with max length of 4.
* System transactions will be always bigger than that.
*
* @param transaction the consensus transaction to check
* @return true if the transaction bytes are system ones, false otherwise
*/
private boolean areTransactionBytesSystemOnes(final Transaction transaction) {
return transaction.getApplicationTransaction().length() > 4;
}

/**
* Converts a transaction to a {@link StateSignatureTransaction} and then consumes it into a callback.
*
* @param transaction the transaction to consume
* @param event the event that contains the transaction
* @param stateSignatureTransactionCallback the callback to call with the system transaction
*/
private void consumeSystemTransaction(
final Transaction transaction,
final Event event,
final Consumer<ScopedSystemTransaction<StateSignatureTransaction>> stateSignatureTransactionCallback) {
try {
final var stateSignatureTransaction =
StateSignatureTransaction.PROTOBUF.parse(transaction.getApplicationTransaction());
stateSignatureTransactionCallback.accept(new ScopedSystemTransaction<>(
event.getCreatorId(), event.getSoftwareVersion(), stateSignatureTransaction));
} catch (final com.hedera.pbj.runtime.ParseException e) {
logger.error("Failed to parse StateSignatureTransaction", e);
}
}

/**
* Apply a transaction to the state.
*
* @param transaction the transaction to apply
*/
private void handleTransaction(@NonNull final ConsensusTransaction transaction) {
if (transaction.isSystem()) {
return;
}
final int delta =
ByteUtils.byteArrayToInt(transaction.getApplicationTransaction().toByteArray(), 0);
runningSum += delta;
Expand Down
Loading

0 comments on commit f26286a

Please sign in to comment.