Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import static com.hedera.hapi.node.base.HederaFunctionality.TOKEN_MINT;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

Expand Down Expand Up @@ -39,6 +40,7 @@
import com.hedera.hapi.platform.state.NodeId;
import com.hedera.node.app.fixtures.state.FakeState;
import com.hedera.node.app.service.consensus.ConsensusService;
import com.hedera.node.app.service.consensus.ReadableTopicStore;
import com.hedera.node.app.service.consensus.impl.schemas.V0490ConsensusSchema;
import com.hedera.node.app.service.contract.ContractService;
import com.hedera.node.app.service.contract.impl.schemas.V0490ContractSchema;
Expand All @@ -51,6 +53,7 @@
import com.hedera.node.app.service.token.TokenService;
import com.hedera.node.app.service.token.impl.schemas.V0490TokenSchema;
import com.hedera.node.app.service.token.impl.schemas.V0530TokenSchema;
import com.hedera.node.app.spi.store.ReadableStoreFactory;
import com.hedera.node.app.store.ReadableStoreFactoryImpl;
import com.hedera.node.app.workflows.TransactionInfo;
import com.hedera.node.config.ConfigProvider;
Expand Down Expand Up @@ -474,6 +477,48 @@ void testCurrentMultiplierConsensusCreateTopic() {
assertEquals(SOME_MULTIPLIER * ENTITY_SCALE_FACTOR, multiplier);
}

@Test
void testCurrentMultiplierConsensusCreateTopicBelowFirstTierAtNewCeiling() {
given(configProvider.getConfiguration()).willReturn(configuration);
given(configuration.getConfigData(FeesConfig.class)).willReturn(feesConfig);
given(feesConfig.percentUtilizationScaleFactors()).willReturn(entityScaleFactors);
given(configuration.getConfigData(TopicsConfig.class)).willReturn(topicsConfig);
given(topicsConfig.maxNumber()).willReturn(2_000_000L);

when(txnInfo.functionality()).thenReturn(CONSENSUS_CREATE_TOPIC);
when(delegate.currentMultiplier()).thenReturn(SOME_MULTIPLIER);

final var topicStore = mock(ReadableTopicStore.class);
when(topicStore.sizeOfState()).thenReturn(19_999L);
final var storeFactory = mock(ReadableStoreFactory.class);
when(storeFactory.readableStore(ReadableTopicStore.class)).thenReturn(topicStore);

long multiplier = utilizationScaledThrottleMultiplier.currentMultiplier(txnInfo, storeFactory);

assertEquals(SOME_MULTIPLIER, multiplier);
}

@Test
void testCurrentMultiplierConsensusCreateTopicCrossesTiersAtNewCeiling() {
given(configProvider.getConfiguration()).willReturn(configuration);
given(configuration.getConfigData(FeesConfig.class)).willReturn(feesConfig);
given(feesConfig.percentUtilizationScaleFactors()).willReturn(entityScaleFactors);
given(configuration.getConfigData(TopicsConfig.class)).willReturn(topicsConfig);
given(topicsConfig.maxNumber()).willReturn(2_000_000L);

when(txnInfo.functionality()).thenReturn(CONSENSUS_CREATE_TOPIC);
when(delegate.currentMultiplier()).thenReturn(SOME_MULTIPLIER);

final var topicStore = mock(ReadableTopicStore.class);
when(topicStore.sizeOfState()).thenReturn(1_000_000L);
final var storeFactory = mock(ReadableStoreFactory.class);
when(storeFactory.readableStore(ReadableTopicStore.class)).thenReturn(topicStore);

long multiplier = utilizationScaledThrottleMultiplier.currentMultiplier(txnInfo, storeFactory);

assertEquals(SOME_MULTIPLIER * 25L, multiplier);
}

@Test
void testCurrentMultiplierDefaultFunctionality() {
given(configProvider.getConfiguration()).willReturn(configuration);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@

@ConfigData("topics")
public record TopicsConfig(
@ConfigProperty(defaultValue = "1000000") @NetworkProperty long maxNumber,
@ConfigProperty(defaultValue = "10") @NetworkProperty int maxCustomFeeEntriesForTopics,
@ConfigProperty(defaultValue = "10") @NetworkProperty int maxEntriesForFeeExemptKeyList) {}
@ConfigProperty(defaultValue = "2000000") @NetworkProperty
long maxNumber,

@ConfigProperty(defaultValue = "10") @NetworkProperty
int maxCustomFeeEntriesForTopics,

@ConfigProperty(defaultValue = "10") @NetworkProperty
int maxEntriesForFeeExemptKeyList) {}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import static com.hedera.services.bdd.junit.EmbeddedReason.NEEDS_STATE_ACCESS;
import static com.hedera.services.bdd.spec.HapiSpec.hapiTest;
import static com.hedera.services.bdd.spec.queries.QueryVerbs.getTxnRecord;
import static com.hedera.services.bdd.spec.transactions.TxnVerbs.createTopic;
import static com.hedera.services.bdd.spec.transactions.TxnVerbs.cryptoCreate;
import static com.hedera.services.bdd.spec.transactions.TxnVerbs.mintToken;
import static com.hedera.services.bdd.spec.transactions.TxnVerbs.tokenCreate;
Expand Down Expand Up @@ -79,10 +80,53 @@ final Stream<DynamicTest> nftPriceScalesWithUtilization() {
.toArray(HapiSpecOperation[]::new)));
}

private long expectedMultiplier(final int mintNo) {
if (mintNo <= 5) {
@LeakyEmbeddedHapiTest(
reason = NEEDS_STATE_ACCESS,
overrides = {"topics.maxNumber", "fees.percentUtilizationScaleFactors"})
final Stream<DynamicTest> topicPriceScalesWithUtilization() {
final var civilian = "topicCivilian";
final var maxAllowed = 10;
final IntFunction<String> createOp = i -> "createTopic" + i;
final AtomicLong baseFee = new AtomicLong();
return hapiTest(
overridingTwo(
"topics.maxNumber",
"" + maxAllowed,
"fees.percentUtilizationScaleFactors",
"TOPIC(0,1:1,50,5:1,90,50:1)"),
cryptoCreate(civilian).balance(ONE_MILLION_HBARS),
blockingOrder(IntStream.range(1, maxAllowed + 1)
.mapToObj(i -> createTopic("topic" + i)
.payingWith(civilian)
.blankMemo()
.fee(1000 * ONE_HUNDRED_HBARS)
.via(createOp.apply(i)))
.toArray(HapiSpecOperation[]::new)),
blockingOrder(IntStream.range(1, maxAllowed + 1)
.mapToObj(i -> getTxnRecord(createOp.apply(i))
.noLogging()
.loggingOnlyFee()
.exposingTo(createRecord -> {
if (i == 1) {
baseFee.set(createRecord.getTransactionFee());
} else {
final var multiplier = expectedMultiplier(i);
final var expected = multiplier * baseFee.get();
assertCloseEnough(
expected,
createRecord.getTransactionFee(),
0.1,
"transaction fee",
multiplier + "x multiplier should be in effect at " + i + " topics");
}
}))
.toArray(HapiSpecOperation[]::new)));
}

private long expectedMultiplier(final int usageNo) {
if (usageNo <= 5) {
return 1;
} else if (mintNo <= 9) {
} else if (usageNo <= 9) {
return 5;
} else {
return 50;
Expand Down
Loading