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

test: Add message submit tests 129-145 #17022

Merged
merged 124 commits into from
Jan 20, 2025
Merged
Show file tree
Hide file tree
Changes from 83 commits
Commits
Show all changes
124 commits
Select commit Hold shift + click to select a range
98a7ac9
Topic get info changes
ibankov Sep 5, 2024
a1e1257
add temporary fix for topic update unit test
ibankov Sep 5, 2024
36d7f4b
Update documentation on protobuf changes
kimbor Sep 8, 2024
dca8c74
Add custom fees to ConsensusCreateTopicHandler
JivkoKelchev Sep 9, 2024
773cbcd
Refactor
JivkoKelchev Sep 10, 2024
e67f50b
consensus custom fee builders and asserts for hapi tests
JivkoKelchev Sep 10, 2024
b9c05bd
Topic create hapi tests
JivkoKelchev Sep 10, 2024
3e4c95f
Merge branch 'refs/heads/develop' into hip-991-topic-fees-get-info
JivkoKelchev Sep 10, 2024
8f48c49
Merge branch 'refs/heads/hip-991-topic-fees-get-info' into hip-991-to…
JivkoKelchev Sep 10, 2024
960cae5
Fix checkModuleDirectivesScope error
JivkoKelchev Sep 10, 2024
a97d4ca
Use copyBuilder()
JivkoKelchev Sep 10, 2024
80c29cb
Fix indentation
JivkoKelchev Sep 10, 2024
54bf31d
Topic create hapi tests
JivkoKelchev Sep 11, 2024
904afa8
Update protobuf messages
JivkoKelchev Sep 11, 2024
3508749
Update protobufs specification text
JivkoKelchev Sep 12, 2024
b695b0d
Merge branch 'refs/heads/hip-991-topic-fees-get-info' into hip-991-to…
JivkoKelchev Sep 12, 2024
a720252
Replace FMLK with FEKL in HapiGetTopicInfo
JivkoKelchev Sep 12, 2024
ee8e728
Merge branch 'refs/heads/hip-991-topic-fees-get-info' into hip-991-to…
JivkoKelchev Sep 12, 2024
0f9a897
Replace FMLK with FEKL in HapiGetTopicInfo
JivkoKelchev Sep 12, 2024
e52365f
Add FEKL and custom fee amount validation
JivkoKelchev Sep 13, 2024
c54f1f5
fix tests
JivkoKelchev Sep 15, 2024
63687f0
spotless
JivkoKelchev Sep 15, 2024
b00e1e8
Merge branch 'refs/heads/hip-991-topic-fees' into hip-991-topic-fees-…
JivkoKelchev Sep 17, 2024
c339784
fix merge conflicts
JivkoKelchev Sep 17, 2024
25ee202
Topic allowances protobufs
ibankov Sep 17, 2024
0ed094c
extend protobufs
ibankov Sep 19, 2024
9ddc6a1
consensus approve allowance proto
ibankov Sep 19, 2024
f473cbb
consensus approve allowance handler
ibankov Sep 19, 2024
11b7bb4
api permissions config
ibankov Sep 19, 2024
1ed4d19
validator and unit tests
ibankov Sep 19, 2024
d620eb6
fix protos and add builders
ibankov Sep 19, 2024
ee2a6b4
fix indentation
ibankov Sep 19, 2024
54afd45
improving descriptions of allowance lists
ibankov Sep 19, 2024
f4cf054
add further documentation
ibankov Sep 19, 2024
c2fdf52
Add grcp endpoint
ibankov Sep 19, 2024
af5e2e3
addressing PR comments
ibankov Sep 20, 2024
34dba09
Merge branch 'hip-991-approve-allowance-proto' into hip-991-allowance…
ibankov Sep 24, 2024
b7c2e84
approve allowance initial commit
ibankov Sep 26, 2024
6633fb8
fix scope
ibankov Sep 26, 2024
32b35c3
add some unit tests
ibankov Sep 26, 2024
9c1f8e9
submit msg
JivkoKelchev Sep 27, 2024
14af58b
Merge branch 'refs/heads/hip-991-allowance-transaction' into hip-991-…
JivkoKelchev Sep 30, 2024
c32fba1
Add hapi tests
JivkoKelchev Sep 30, 2024
ff651bc
Add unit tests and reject contracts
ibankov Oct 1, 2024
38bd23d
Merge branch 'refs/heads/hip-991-allowance-transaction' into hip-991-…
JivkoKelchev Oct 1, 2024
5fec8e3
Split crypto dispatches if we have to many transfers
JivkoKelchev Oct 1, 2024
145a04f
Hapi tests, custom fee assessor refactoring
JivkoKelchev Oct 8, 2024
8598691
Refactor
JivkoKelchev Oct 8, 2024
78c8748
Add hapi tests
JivkoKelchev Oct 8, 2024
dea8af1
Fix tests
JivkoKelchev Oct 10, 2024
ab6ce39
submit msg
JivkoKelchev Sep 27, 2024
b1f07bc
Add hapi tests
JivkoKelchev Sep 30, 2024
83f37e2
Split crypto dispatches if we have to many transfers
JivkoKelchev Oct 1, 2024
5e6d71f
Hapi tests, custom fee assessor refactoring
JivkoKelchev Oct 8, 2024
2cca738
Refactor
JivkoKelchev Oct 8, 2024
44ff622
Add hapi tests
JivkoKelchev Oct 8, 2024
3ef8877
Fix tests
JivkoKelchev Oct 10, 2024
f1de34a
feat: Add custom fees to ConsensusCreateTopicHandler (#15402)
JivkoKelchev Oct 3, 2024
becfa7e
feat: Update ConsensusUpdateTopicHandler with new custom fee function…
vtronkov Oct 3, 2024
cc1d011
fix merge conflict
JivkoKelchev Dec 5, 2024
10dc471
Merge remote-tracking branch 'origin/hip-991-submit-message' into hip…
JivkoKelchev Dec 5, 2024
7483da7
Remove allowances
JivkoKelchev Dec 5, 2024
158ab5b
Remove allowances
JivkoKelchev Dec 5, 2024
fa0445f
Remove allowances
JivkoKelchev Dec 5, 2024
ca60528
Remove allowances
JivkoKelchev Dec 6, 2024
d428b8c
Revert ConsensusCreateTopicHandler
JivkoKelchev Dec 6, 2024
7d5da3a
Remove allowances
JivkoKelchev Dec 6, 2024
ecc3e93
Merge branch 'refs/heads/hip-991-topic-fees' into hip-991-submit-message
JivkoKelchev Dec 6, 2024
e222736
Fix merge conflicts
JivkoKelchev Dec 6, 2024
a0446e0
Revert changes
JivkoKelchev Dec 9, 2024
db637af
Fix dispatching of child transfers
JivkoKelchev Dec 9, 2024
d1b1714
fix unit tests
JivkoKelchev Dec 9, 2024
d472977
Add max_custom_fees and accept_all_custom_fees
JivkoKelchev Dec 10, 2024
2570e43
Revert unnecessary changes
JivkoKelchev Dec 10, 2024
ae381ba
Refactor custom fee validations
JivkoKelchev Dec 10, 2024
0705691
Cleanup
JivkoKelchev Dec 10, 2024
51c7183
add CUSTOM_FEES_LIMIT_EXCEEDED status code
JivkoKelchev Dec 11, 2024
1dfd412
Merge branch 'refs/heads/hip-991-topic-fees' into hip-991-submit-message
JivkoKelchev Dec 11, 2024
5a6f72c
Simplify the fee assessment
JivkoKelchev Dec 11, 2024
9a3c4e1
Add message submit tests 129-145
ibankov Dec 11, 2024
02b3fc9
asdd
ibankov Dec 11, 2024
7773580
spot
ibankov Dec 11, 2024
22de1f8
description
ibankov Dec 11, 2024
4a9f444
Add unit test
JivkoKelchev Dec 11, 2024
b548295
fix module-info.java
JivkoKelchev Dec 12, 2024
5f0acd2
fix module-info.java
JivkoKelchev Dec 12, 2024
7ddd522
fix limit comparison
JivkoKelchev Dec 12, 2024
5c1b466
Merge branch 'hip-991-submit-message' into hip-991-submit-message-129…
ibankov Dec 13, 2024
29dfbba
revert HapiTopicCreate changes
JivkoKelchev Dec 13, 2024
44f9842
Fee limits additional validations
JivkoKelchev Dec 16, 2024
2be0067
Fee limits additional validations
JivkoKelchev Dec 16, 2024
ac943c5
fixed fee limit validation
ibankov Dec 16, 2024
b2dc290
Merge branch 'hip-991-submit-message' into hip-991-submit-message-129…
ibankov Dec 16, 2024
b103957
Merge branch 'hip-991-topic-fees' into hip-991-submit-message
ibankov Dec 17, 2024
c2c4f48
Remove draft test
JivkoKelchev Dec 17, 2024
3afeacd
Filter only payable fees and use this list for limit validation and c…
JivkoKelchev Dec 19, 2024
1379127
Merge branch 'refs/heads/hip-991-topic-fees' into hip-991-submit-message
JivkoKelchev Dec 19, 2024
a9d333a
Fix docs
JivkoKelchev Dec 19, 2024
d9fab19
Merge branch 'hip-991-topic-fees' into hip-991-submit-message
ibankov Jan 2, 2025
28a8f2f
Merge branch 'hip-991-topic-fees' into hip-991-submit-message
ibankov Jan 2, 2025
e236632
spotless
ibankov Jan 2, 2025
42abcdc
Merge branch 'hip-991-submit-message' into hip-991-submit-message-129…
ibankov Jan 2, 2025
2c5413d
Replace FEKL keys validation
JivkoKelchev Jan 6, 2025
c0880ab
Merge remote-tracking branch 'origin/hip-991-submit-message' into hip…
JivkoKelchev Jan 6, 2025
29da0fb
Replace FEKL keys validation
JivkoKelchev Jan 6, 2025
700acbc
Fix unit tests
JivkoKelchev Jan 6, 2025
07d571e
Added more coverage
ibankov Jan 6, 2025
35bac65
java doc
ibankov Jan 7, 2025
aa773a6
Update protobufs
JivkoKelchev Jan 10, 2025
ad28cb8
Merge branch 'refs/heads/hip-991-protobuf-changes' into hip-991-submi…
JivkoKelchev Jan 10, 2025
063d698
Update protobufs
JivkoKelchev Jan 10, 2025
abb9118
Update doc
JivkoKelchev Jan 13, 2025
eae0d98
Merge branch 'refs/heads/hip-991-protobuf-changes' into hip-991-submi…
JivkoKelchev Jan 14, 2025
178b878
Merge branch 'hip-991-submit-message' into hip-991-submit-message-129…
ibankov Jan 14, 2025
a616404
Change ordinal of CustomFeeLimit according to HIP changes
JivkoKelchev Jan 14, 2025
9e70e55
Merge branch 'refs/heads/hip-991-protobuf-changes' into hip-991-submi…
JivkoKelchev Jan 14, 2025
8960c6c
Merge branch 'hip-991-submit-message' into hip-991-submit-message-129…
ibankov Jan 14, 2025
bd148f4
fix tests
ibankov Jan 14, 2025
0adfdbe
fix test name
ibankov Jan 14, 2025
d64317c
Merge branch 'refs/heads/hip-991-topic-fees' into hip-991-protobuf-ch…
JivkoKelchev Jan 14, 2025
45fb7a9
Merge branch 'refs/heads/hip-991-protobuf-changes' into hip-991-submi…
JivkoKelchev Jan 14, 2025
061420e
Merge branch 'hip-991-submit-message' into hip-991-submit-message-129…
ibankov Jan 14, 2025
71bf501
Merge branch 'hip-991-topic-fees' into hip-991-submit-message-129-145
ibankov Jan 20, 2025
7470de9
merge conflicts
ibankov Jan 20, 2025
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
11 changes: 11 additions & 0 deletions hapi/hedera-protobufs/services/consensus_submit_message.proto
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ option java_package = "com.hederahashgraph.api.proto.java";
option java_multiple_files = true;

import "basic_types.proto";
import "custom_fees.proto";

/**
* UNDOCUMENTED
Expand Down Expand Up @@ -66,4 +67,14 @@ message ConsensusSubmitMessageTransactionBody {
* Optional information of the current chunk in a fragmented message.
*/
ConsensusMessageChunkInfo chunkInfo = 3;

/**
* The maximum custom fee that the user is willing to pay for the message. This field will be ignored if `accept_all_custom_fees` is set to `true`.
*/
repeated FixedFee max_custom_fees = 4;

/**
* If set to true, the transaction will accept all custom fees from the topic id
*/
bool accept_all_custom_fees = 5;
}
27 changes: 16 additions & 11 deletions hapi/hedera-protobufs/services/response_code.proto
Original file line number Diff line number Diff line change
Expand Up @@ -1613,29 +1613,34 @@ enum ResponseCodeEnum {
MISSING_EXPIRY_TIME = 372;

/**
* The provided fee exempt key list size exceeded the limit.
*/
* The provided fee exempt key list size exceeded the limit.
*/
MAX_ENTRIES_FOR_FEE_EXEMPT_KEY_LIST_EXCEEDED = 373;

/**
* The provided fee exempt key list contains duplicated keys.
*/
* The provided fee exempt key list contains duplicated keys.
*/
FEE_EXEMPT_KEY_LIST_CONTAINS_DUPLICATED_KEYS = 374;

/**
* The provided fee exempt key list contains an invalid key.
*/
* The provided fee exempt key list contains an invalid key.
*/
INVALID_KEY_IN_FEE_EXEMPT_KEY_LIST = 375;

/**
* The provided fee schedule key contains an invalid key.
*/
* The provided fee schedule key contains an invalid key.
*/
INVALID_FEE_SCHEDULE_KEY = 376;

/**
* If a fee schedule key is not set when we create a topic
* we cannot add it on update.
*/
* If a fee schedule key is not set when we create a topic
* we cannot add it on update.
*/
FEE_SCHEDULE_KEY_CANNOT_BE_UPDATED = 377;

/**
* The fee amount is exceeding the amount that the payer
* is willing to pay.
*/
CUSTOM_FEES_LIMIT_EXCEEDED = 378;
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,29 +23,40 @@
import static com.hedera.hapi.node.base.ResponseCodeEnum.INVALID_TOPIC_MESSAGE;
import static com.hedera.hapi.node.base.ResponseCodeEnum.INVALID_TRANSACTION;
import static com.hedera.hapi.node.base.ResponseCodeEnum.MESSAGE_SIZE_TOO_LARGE;
import static com.hedera.hapi.node.base.ResponseCodeEnum.SUCCESS;
import static com.hedera.node.app.hapi.utils.fee.FeeBuilder.BASIC_ENTITY_ID_SIZE;
import static com.hedera.node.app.hapi.utils.fee.FeeBuilder.LONG_SIZE;
import static com.hedera.node.app.hapi.utils.fee.FeeBuilder.RECEIPT_STORAGE_TIME_SEC;
import static com.hedera.node.app.hapi.utils.fee.FeeBuilder.TX_HASH_SIZE;
import static com.hedera.node.app.spi.validation.Validations.mustExist;
import static com.hedera.node.app.spi.workflows.DispatchOptions.stepDispatch;
import static com.hedera.node.app.spi.workflows.HandleException.validateTrue;
import static com.hedera.node.app.spi.workflows.PreCheckException.validateFalsePreCheck;
import static com.hedera.node.app.spi.workflows.PreCheckException.validateTruePreCheck;
import static com.hedera.node.app.spi.workflows.record.StreamBuilder.TransactionCustomizer.NOOP_TRANSACTION_CUSTOMIZER;
import static java.util.Objects.requireNonNull;

import com.hedera.hapi.node.base.AccountID;
import com.hedera.hapi.node.base.HederaFunctionality;
import com.hedera.hapi.node.base.Key;
import com.hedera.hapi.node.base.ResponseCodeEnum;
import com.hedera.hapi.node.base.SubType;
import com.hedera.hapi.node.base.TopicID;
import com.hedera.hapi.node.base.TransactionID;
import com.hedera.hapi.node.consensus.ConsensusSubmitMessageTransactionBody;
import com.hedera.hapi.node.state.consensus.Topic;
import com.hedera.hapi.node.transaction.ConsensusCustomFee;
import com.hedera.hapi.node.transaction.FixedFee;
import com.hedera.hapi.node.transaction.TransactionBody;
import com.hedera.node.app.hapi.utils.CommonPbjConverters;
import com.hedera.node.app.service.consensus.ReadableTopicStore;
import com.hedera.node.app.service.consensus.impl.WritableTopicStore;
import com.hedera.node.app.service.consensus.impl.handlers.customfee.ConsensusCustomFeeAssessor;
import com.hedera.node.app.service.consensus.impl.records.ConsensusSubmitMessageStreamBuilder;
import com.hedera.node.app.service.token.records.CryptoTransferStreamBuilder;
import com.hedera.node.app.spi.fees.FeeContext;
import com.hedera.node.app.spi.fees.Fees;
import com.hedera.node.app.spi.key.KeyVerifier;
import com.hedera.node.app.spi.workflows.HandleContext;
import com.hedera.node.app.spi.workflows.HandleException;
import com.hedera.node.app.spi.workflows.PreCheckException;
Expand All @@ -61,6 +72,8 @@
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.time.Instant;
import java.util.HashSet;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Singleton;

Expand All @@ -75,12 +88,14 @@ public class ConsensusSubmitMessageHandler implements TransactionHandler {
*/
public static final long RUNNING_HASH_VERSION = 3L;

private final ConsensusCustomFeeAssessor customFeeAssessor;

/**
* Default constructor for injection.
*/
@Inject
public ConsensusSubmitMessageHandler() {
// Exists for injection
public ConsensusSubmitMessageHandler(@NonNull ConsensusCustomFeeAssessor customFeeAssessor) {
this.customFeeAssessor = requireNonNull(customFeeAssessor);
}

@Override
Expand All @@ -105,6 +120,12 @@ public void preHandle(@NonNull final PreHandleContext context) throws PreCheckEx
if (topic.hasSubmitKey()) {
context.requireKeyOrThrow(topic.submitKeyOrThrow(), INVALID_SUBMIT_KEY);
}
// add optional fee exempt keys in to the key verifieer
// later it will be used to validate if transaction was signed by
// any of these keys and based on that, custom fees will be charged or not
if (!topic.feeExemptKeyList().isEmpty()) {
context.optionalKeys(new HashSet<>(topic.feeExemptKeyList()));
}
}

/**
Expand All @@ -128,6 +149,26 @@ public void handle(@NonNull final HandleContext handleContext) {
final var config = handleContext.configuration().getConfigData(ConsensusConfig.class);
validateTransaction(txn, config, topic);

/* handle custom fees */
if (!topic.customFees().isEmpty() && !isFeeExempted(topic.feeExemptKeyList(), handleContext.keyVerifier())) {
// check payer limits or throw
if (!op.acceptAllCustomFees()) {
validateFeeLimits(topic.customFees(), op.maxCustomFees());
}
// create synthetic body and dispatch crypto transfer
final var syntheticBodies = customFeeAssessor.assessCustomFee(topic, handleContext);
for (final var syntheticBody : syntheticBodies) {
final var record = handleContext.dispatch(stepDispatch(
handleContext.payer(),
TransactionBody.newBuilder()
.cryptoTransfer(syntheticBody)
.build(),
CryptoTransferStreamBuilder.class,
NOOP_TRANSACTION_CUSTOMIZER));
validateTrue(record.status().equals(SUCCESS), record.status());
}
}

try {
final var updatedTopic = updateRunningHashAndSequenceNumber(txn, topic, handleContext.consensusNow());

Expand Down Expand Up @@ -280,6 +321,60 @@ public static byte[] noThrowSha384HashOf(final byte[] byteArray) {
}
}

/**
* Check if the submit message transaction is fee exempt
*
* @param feeExemptKeyList The list of keys that are exempt from fees
* @param keyVerifier The key verifier of this transaction
* @return if the transaction is fee exempt
*/
private boolean isFeeExempted(@NonNull final List<Key> feeExemptKeyList, @NonNull final KeyVerifier keyVerifier) {
if (!feeExemptKeyList.isEmpty()) {
for (final var key : feeExemptKeyList) {
final var keyVerificationResult = keyVerifier.verificationFor(key);
if (keyVerificationResult.passed()) {
return true;
}
}
}
return false;
}

/**
* Validate that each topic custom fee has equal or lower value than the payer's limit
*
* @param topicCustomFees The topic's custom fee list
* @param payerCustomFeeLimits List with limits of fees that the payer is willing to pay
*/
private void validateFeeLimits(
@NonNull final List<ConsensusCustomFee> topicCustomFees,
@NonNull final List<FixedFee> payerCustomFeeLimits) {
for (final ConsensusCustomFee fee : topicCustomFees) {
// validate limits
boolean passed = false;
final var fixedFee = fee.fixedFeeOrThrow();
if (fixedFee.hasDenominatingTokenId()) {
for (FixedFee feeLimit : payerCustomFeeLimits) {
if (feeLimit.hasDenominatingTokenId()
&& feeLimit.denominatingTokenId().equals(fixedFee.denominatingTokenId())
&& fixedFee.amount() <= feeLimit.amount()) {
ibankov marked this conversation as resolved.
Show resolved Hide resolved
passed = true;
break;
}
}
} else {
for (FixedFee feeLimit : payerCustomFeeLimits) {
if (!feeLimit.hasDenominatingTokenId() && feeLimit.amount() <= fixedFee.amount()) {
passed = true;
break;
}
}
}
// if any fee amount is larger than the corresponding limit, validation should fail
validateTrue(passed, ResponseCodeEnum.CUSTOM_FEES_LIMIT_EXCEEDED);
}
}

@NonNull
@Override
public Fees calculateFees(@NonNull final FeeContext feeContext) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
/*
* Copyright (C) 2024 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.
*/

package com.hedera.node.app.service.consensus.impl.handlers.customfee;

import static com.hedera.node.app.spi.workflows.HandleException.validateTrue;

import com.hedera.hapi.node.base.AccountAmount;
import com.hedera.hapi.node.base.AccountID;
import com.hedera.hapi.node.base.ResponseCodeEnum;
import com.hedera.hapi.node.base.TokenID;
import com.hedera.hapi.node.base.TokenTransferList;
import com.hedera.hapi.node.base.TransferList;
import com.hedera.hapi.node.state.consensus.Topic;
import com.hedera.hapi.node.token.CryptoTransferTransactionBody;
import com.hedera.hapi.node.transaction.ConsensusCustomFee;
import com.hedera.hapi.node.transaction.FixedFee;
import com.hedera.node.app.service.token.ReadableTokenStore;
import com.hedera.node.app.spi.workflows.HandleContext;
import edu.umd.cs.findbugs.annotations.NonNull;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Singleton;

@Singleton
public class ConsensusCustomFeeAssessor {

/**
* Constructs a {@link ConsensusCustomFeeAssessor} instance.
*/
@Inject
public ConsensusCustomFeeAssessor() {
// Needed for Dagger injection
}

/**
* Build and return a list of synthetic crypto transfer transaction bodies, that represents custom fees payments.
* It will return one body per topic custom fee.
*
* @param topic The topic
* @param context The transaction handle context
* @return List of synthetic crypto transfer transaction bodies
*/
public List<CryptoTransferTransactionBody> assessCustomFee(
@NonNull final Topic topic, @NonNull final HandleContext context) {
final List<CryptoTransferTransactionBody> transactionBodies = new ArrayList<>();
final var payer = context.payer();
final var tokenStore = context.storeFactory().readableStore(ReadableTokenStore.class);

// build crypto transfer bodies for the first layer of custom fees,
// if there is a second or third layer it will be assessed in crypto transfer handler
for (ConsensusCustomFee fee : topic.customFees()) {

// check if payer is collector
if (context.payer().equals(fee.feeCollectorAccountId())) {
continue;
}

final var tokenTransfers = new ArrayList<TokenTransferList>();
List<AccountAmount> hbarTransfers = new ArrayList<>();

final var fixedFee = fee.fixedFeeOrThrow();
if (fixedFee.hasDenominatingTokenId()) {
final var tokenId = fixedFee.denominatingTokenIdOrThrow();
final var tokenTreasury = getTokenTreasury(tokenId, tokenStore);
// check if payer is treasury
if (context.payer().equals(tokenTreasury)) {
continue;
}
tokenTransfers.add(buildCustomFeeTokenTransferList(payer, fee.feeCollectorAccountId(), fixedFee));
} else {
hbarTransfers = buildCustomFeeHbarTransferList(payer, fee.feeCollectorAccountId(), fixedFee);
}

// build the synthetic body
final var syntheticBodyBuilder =
CryptoTransferTransactionBody.newBuilder().tokenTransfers(tokenTransfers);
transactionBodies.add(syntheticBodyBuilder
.transfers(TransferList.newBuilder().accountAmounts(hbarTransfers.toArray(AccountAmount[]::new)))
.build());
}

return transactionBodies;
}

private List<AccountAmount> buildCustomFeeHbarTransferList(AccountID payer, AccountID collector, FixedFee fee) {
return List.of(
AccountAmount.newBuilder()
.accountID(payer)
.amount(-fee.amount())
.build(),
AccountAmount.newBuilder()
.accountID(collector)
.amount(fee.amount())
.build());
}

private TokenTransferList buildCustomFeeTokenTransferList(AccountID payer, AccountID collector, FixedFee fee) {
return TokenTransferList.newBuilder()
.token(fee.denominatingTokenId())
.transfers(
AccountAmount.newBuilder()
.accountID(payer)
.amount(-fee.amount())
.build(),
AccountAmount.newBuilder()
.accountID(collector)
.amount(fee.amount())
.build())
.build();
}

private AccountID getTokenTreasury(TokenID tokenId, ReadableTokenStore tokenStore) {
final var token = tokenStore.get(tokenId);
validateTrue(token != null, ResponseCodeEnum.INVALID_TOKEN_ID_IN_CUSTOM_FEES);
return token.treasuryAccountIdOrThrow();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
com.hedera.node.app,
com.hedera.node.test.clients;
exports com.hedera.node.app.service.consensus.impl.handlers;
exports com.hedera.node.app.service.consensus.impl.handlers.customfee;
exports com.hedera.node.app.service.consensus.impl.records;
exports com.hedera.node.app.service.consensus.impl.schemas;
exports com.hedera.node.app.service.consensus.impl.validators;
Expand Down
Loading