Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrated ScheduleService to new design #6243

Merged
merged 8 commits into from
Apr 28, 2023
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,9 @@
import com.hedera.node.app.service.consensus.ConsensusService;
import com.hedera.node.app.service.consensus.ReadableTopicStore;
import com.hedera.node.app.service.consensus.impl.ReadableTopicStoreImpl;
import com.hedera.node.app.service.schedule.ReadableScheduleStore;
import com.hedera.node.app.service.schedule.ScheduleService;
import com.hedera.node.app.service.schedule.impl.ReadableScheduleStore;
import com.hedera.node.app.service.schedule.impl.ReadableScheduleStoreImpl;
import com.hedera.node.app.service.token.ReadableAccountStore;
import com.hedera.node.app.service.token.ReadableTokenStore;
import com.hedera.node.app.service.token.TokenService;
Expand Down Expand Up @@ -53,7 +54,7 @@ AccountAccess.class, new StoreEntry(TokenService.NAME, ReadableAccountStoreImpl:
ReadableAccountStore.class, new StoreEntry(TokenService.NAME, ReadableAccountStoreImpl::new),
ReadableTokenStore.class, new StoreEntry(TokenService.NAME, ReadableTokenStoreImpl::new),
ReadableTopicStore.class, new StoreEntry(ConsensusService.NAME, ReadableTopicStoreImpl::new),
ReadableScheduleStore.class, new StoreEntry(ScheduleService.NAME, ReadableScheduleStore::new),
ReadableScheduleStore.class, new StoreEntry(ScheduleService.NAME, ReadableScheduleStoreImpl::new),
ReadableSpecialFileStore.class, new StoreEntry(FreezeService.NAME, ReadableSpecialFileStoreImpl::new));

private final HederaState state;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2022-2023 Hedera Hashgraph, LLC
* Copyright (C) 2023 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.
Expand All @@ -16,75 +16,53 @@

package com.hedera.node.app.service.schedule.impl;

import static com.hedera.node.app.service.mono.pbj.PbjConverter.asPbjKey;
import static com.hedera.node.app.service.mono.pbj.PbjConverter.toPbj;

import com.hedera.hapi.node.base.AccountID;
import com.hedera.hapi.node.base.Key;
import com.hedera.hapi.node.base.ScheduleID;
import com.hedera.hapi.node.transaction.TransactionBody;
import com.hedera.node.app.service.mono.pbj.PbjConverter;
import com.hedera.node.app.service.mono.state.virtual.schedule.ScheduleVirtualValue;
import com.hedera.node.app.service.schedule.ReadableScheduleStore;
import com.hedera.node.app.spi.state.ReadableKVState;
import com.hedera.node.app.spi.state.ReadableStates;
import edu.umd.cs.findbugs.annotations.NonNull;
import java.util.Objects;
import java.util.Optional;

/**
* Provides read-only methods for interacting with the underlying data storage mechanisms for
* working with Schedules. If the scheduleID is valid and a schedule exists returns {@link
* ScheduleVirtualValue}.
*
* <p>This class is not exported from the module. It is an internal implementation detail.
* Default implementation of {@link ReadableScheduleStore}.
*/
public class ReadableScheduleStore {
public class ReadableScheduleStoreImpl implements ReadableScheduleStore {
/** The underlying data storage class that holds the token data. */
private final ReadableKVState<Long, ScheduleVirtualValue> schedulesById;

/**
* Create a new {@link ReadableScheduleStore} instance.
* Create a new {@link ReadableScheduleStoreImpl} instance.
*
* @param states The state to use.
*/
public ReadableScheduleStore(@NonNull final ReadableStates states) {
public ReadableScheduleStoreImpl(@NonNull final ReadableStates states) {
Objects.requireNonNull(states);
this.schedulesById = states.get("SCHEDULES_BY_ID");
}

/**
* Gets the schedule with the given {@link ScheduleID}. If there is no schedule with given ID
* returns {@link Optional#empty()}.
*
* @param id given id for the schedule
* @return the schedule with the given id
*/
public Optional<ScheduleMetadata> get(final ScheduleID id) {
@Override
@NonNull
public Optional<ScheduleMetadata> get(@NonNull final ScheduleID id) {
final var schedule = schedulesById.get(id.scheduleNum());
if (schedule == null) {
return Optional.empty();
}
final Key adminKey;
if (schedule.hasAdminKey()) {
adminKey = asPbjKey(schedule.adminKey().get());
adminKey = PbjConverter.asPbjKey(schedule.adminKey().get());
} else {
adminKey = null;
}
return Optional.ofNullable(schedule)
.map(s -> new ScheduleMetadata(
adminKey,
toPbj(schedule.ordinaryViewOfScheduledTxn()),
PbjConverter.toPbj(schedule.ordinaryViewOfScheduledTxn()),
schedule.hasExplicitPayer()
? Optional.of(schedule.payer().toPbjAccountId())
: Optional.empty()));
}

/**
* Metadata about a schedule.
*
* @param adminKey admin key on the schedule
* @param scheduledTxn scheduled transaction
* @param designatedPayer payer for the schedule execution.If there is no explicit payer,
* returns {@link Optional#empty()}.
*/
public record ScheduleMetadata(Key adminKey, TransactionBody scheduledTxn, Optional<AccountID> designatedPayer) {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,22 @@
import com.hedera.node.app.spi.workflows.PreCheckException;
import com.hedera.node.app.spi.workflows.PreHandleContext;
import com.hedera.node.app.spi.workflows.PreHandleDispatcher;
import java.util.Objects;

/**
* Provides some implementation support needed for both the {@link ScheduleCreateHandler} and {@link
* ScheduleSignHandler}.
*/
abstract class AbstractScheduleHandler {

private final PreHandleDispatcher dispatcher;

AbstractScheduleHandler(PreHandleDispatcher dispatcher) {
jsync-swirlds marked this conversation as resolved.
Show resolved Hide resolved
this.dispatcher = Objects.requireNonNull(dispatcher, "The supplied argument 'dispatcher' must not be null.");
}

protected void preHandleScheduledTxn(
final PreHandleContext context,
final TransactionBody scheduledTxn,
final AccountID payerForNested,
final PreHandleDispatcher dispatcher)
final PreHandleContext context, final TransactionBody scheduledTxn, final AccountID payerForNested)
throws PreCheckException {
final var innerContext =
context.createNestedContext(scheduledTxn, payerForNested, UNRESOLVABLE_REQUIRED_SIGNERS);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,24 +38,20 @@
*/
@Singleton
public class ScheduleCreateHandler extends AbstractScheduleHandler implements TransactionHandler {

// @todo('6249') This constructor should be removed and the @Inject annotation added to the remaining constructor
@Inject
public ScheduleCreateHandler() {
// Exists for injection
super(null);

Check warning on line 46 in hedera-node/hedera-schedule-service-impl/src/main/java/com/hedera/node/app/service/schedule/impl/handlers/ScheduleCreateHandler.java

View check run for this annotation

Codecov / codecov/patch

hedera-node/hedera-schedule-service-impl/src/main/java/com/hedera/node/app/service/schedule/impl/handlers/ScheduleCreateHandler.java#L46

Added line #L46 was not covered by tests
jsync-swirlds marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* This method is called during the pre-handle workflow.
*
* <p>Pre-handles a {@link HederaFunctionality#SCHEDULE_CREATE} transaction, returning the
* metadata required to, at minimum, validate the signatures of all required signing keys.
*
* @param context the {@link PreHandleContext} which collects all information
* @param dispatcher the {@link PreHandleDispatcher} that can be used to pre-handle the inner
* txn
* @throws NullPointerException if one of the arguments is {@code null}
*/
public void preHandle(@NonNull final PreHandleContext context, @NonNull final PreHandleDispatcher dispatcher)
throws PreCheckException {
public ScheduleCreateHandler(@NonNull final PreHandleDispatcher dispatcher) {
super(dispatcher);
}

@Override
public void preHandle(@NonNull final PreHandleContext context) throws PreCheckException {
requireNonNull(context);
final var txn = context.body();
final var op = txn.scheduleCreateOrThrow();
Expand All @@ -80,7 +76,7 @@

// FUTURE: Once we allow schedule transactions to be scheduled inside, we need a check here
// to see if provided payer is same as payer in the inner transaction.
preHandleScheduledTxn(context, scheduledTxn, payerForNested, dispatcher);
preHandleScheduledTxn(context, scheduledTxn, payerForNested);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

import com.hedera.hapi.node.base.HederaFunctionality;
import com.hedera.hapi.node.base.ScheduleID;
import com.hedera.node.app.service.schedule.impl.ReadableScheduleStore;
import com.hedera.node.app.service.schedule.ReadableScheduleStore;
import com.hedera.node.app.spi.workflows.PreCheckException;
import com.hedera.node.app.spi.workflows.PreHandleContext;
import com.hedera.node.app.spi.workflows.TransactionHandler;
Expand All @@ -41,25 +41,12 @@ public ScheduleDeleteHandler() {
// Exists for injection
}

/**
* This method is called during the pre-handle workflow.
*
* <p>Pre-handles a {@link HederaFunctionality#SCHEDULE_DELETE} transaction, returning the
* metadata required to, at minimum, validate the signatures of all required signing keys.
*
* <p>Please note: the method signature is just a placeholder which is most likely going to
* change.
*
* @param context the {@link PreHandleContext} which collects all information
*
* @param scheduleStore the {@link ReadableScheduleStore} that contains all scheduled-data
* @throws NullPointerException if one of the arguments is {@code null}
*/
public void preHandle(@NonNull final PreHandleContext context, @NonNull final ReadableScheduleStore scheduleStore)
throws PreCheckException {
@Override
public void preHandle(@NonNull final PreHandleContext context) throws PreCheckException {
requireNonNull(context);
final var op = context.body().scheduleDeleteOrThrow();
final var id = op.scheduleIDOrElse(ScheduleID.DEFAULT);
final var scheduleStore = context.createStore(ReadableScheduleStore.class);

// check for a missing schedule. A schedule with this id could have never existed,
// or it could have already been executed or deleted
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
import com.hedera.hapi.node.base.HederaFunctionality;
import com.hedera.hapi.node.base.ScheduleID;
import com.hedera.hapi.node.base.TransactionID;
import com.hedera.node.app.service.schedule.impl.ReadableScheduleStore;
import com.hedera.node.app.service.schedule.ReadableScheduleStore;
import com.hedera.node.app.spi.workflows.PreCheckException;
import com.hedera.node.app.spi.workflows.PreHandleContext;
import com.hedera.node.app.spi.workflows.PreHandleDispatcher;
Expand All @@ -40,30 +40,20 @@
@Inject
public ScheduleSignHandler() {
// Exists for injection
super(null);

Check warning on line 43 in hedera-node/hedera-schedule-service-impl/src/main/java/com/hedera/node/app/service/schedule/impl/handlers/ScheduleSignHandler.java

View check run for this annotation

Codecov / codecov/patch

hedera-node/hedera-schedule-service-impl/src/main/java/com/hedera/node/app/service/schedule/impl/handlers/ScheduleSignHandler.java#L43

Added line #L43 was not covered by tests
}

/**
* Pre-handles a {@link HederaFunctionality#SCHEDULE_SIGN} transaction, returning the metadata
* required to, at minimum, validate the signatures of all required signing keys.
*
* @param context the {@link PreHandleContext} which collects all information
*
* @param scheduleStore the {@link ReadableScheduleStore} to use for schedule resolution
* @param dispatcher the {@link PreHandleDispatcher} that can be used to pre-handle the inner
* txn
* @throws NullPointerException if one of the arguments is {@code null}
*/
public void preHandle(
@NonNull final PreHandleContext context,
@NonNull final ReadableScheduleStore scheduleStore,
@NonNull final PreHandleDispatcher dispatcher)
throws PreCheckException {
public ScheduleSignHandler(@NonNull final PreHandleDispatcher dispatcher) {
super(dispatcher);
}

@Override
public void preHandle(@NonNull final PreHandleContext context) throws PreCheckException {
requireNonNull(context);
requireNonNull(scheduleStore);
requireNonNull(dispatcher);
final var txn = context.body();
final var op = txn.scheduleSignOrThrow();
final var id = op.scheduleIDOrElse(ScheduleID.DEFAULT);
final var scheduleStore = context.createStore(ReadableScheduleStore.class);

final var scheduleLookupResult = scheduleStore.get(id);
if (scheduleLookupResult.isEmpty()) {
Expand All @@ -75,7 +65,7 @@
final var payerForNested = optionalPayer.orElse(
scheduledTxn.transactionIDOrElse(TransactionID.DEFAULT).accountIDOrElse(AccountID.DEFAULT));

preHandleScheduledTxn(context, scheduledTxn, payerForNested, dispatcher);
preHandleScheduledTxn(context, scheduledTxn, payerForNested);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@
import com.hedera.node.app.service.mono.pbj.PbjConverter;
import com.hedera.node.app.service.mono.state.submerkle.EntityId;
import com.hedera.node.app.service.mono.state.virtual.schedule.ScheduleVirtualValue;
import com.hedera.node.app.service.schedule.impl.ReadableScheduleStore;
import com.hedera.node.app.service.schedule.ReadableScheduleStore;
import com.hedera.node.app.service.schedule.impl.ReadableScheduleStoreImpl;
import com.hedera.node.app.spi.key.HederaKey;
import com.hedera.node.app.spi.state.ReadableKVState;
import com.hedera.node.app.spi.state.ReadableStates;
Expand Down Expand Up @@ -59,12 +60,12 @@ class ReadableScheduleStoreTest {
@BeforeEach
void setUp() {
given(states.get("SCHEDULES_BY_ID")).willReturn(state);
subject = new ReadableScheduleStore(states);
subject = new ReadableScheduleStoreImpl(states);
}

@Test
void constructorThrowsIfStatesIsNull() {
assertThrows(NullPointerException.class, () -> new ReadableScheduleStore(null));
assertThrows(NullPointerException.class, () -> new ReadableScheduleStoreImpl(null));
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,17 @@

package com.hedera.node.app.service.schedule.impl.test;

import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.assertNotNull;

import com.hedera.node.app.service.schedule.impl.components.DaggerScheduleComponent;
import com.hedera.node.app.service.schedule.impl.components.ScheduleComponent;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;

class ScheduleComponentTest {

@Test
@Disabled("DI is not yet wired up")
void objectGraphRootsAreAvailable() {
// given:
ScheduleComponent subject = DaggerScheduleComponent.factory().create();
Expand Down
Loading
Loading