Skip to content

Commit

Permalink
Remove generation of token history row when total supply changes for …
Browse files Browse the repository at this point in the history
…Mint/Wipe/Burn (#7095)

Description:
Remove the creation of history rows when the token.total_supply column is adjusted for token mints, burns, or wipes. History rows are added for create, update, or delete operations on the token entity itself.

Remove setting for timestamp range on the above mentioned token operations.
Add token history row check to existing tests.
Related issue(s):

Fixes #6998
  • Loading branch information
mgoelswirlds authored Oct 31, 2023
1 parent c46c7b9 commit 22f2a8d
Show file tree
Hide file tree
Showing 13 changed files with 270 additions and 108 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
public interface History {

@JsonIgnore
default boolean isHistory() {
default boolean hasHistory() {
return getTimestampRange() != null;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,4 +109,17 @@ public void setName(String name) {
public void setSymbol(String symbol) {
this.symbol = DomainUtils.sanitize(symbol);
}

public void setTotalSupply(Long newTotalSupply) {
if (newTotalSupply == null) {
return;
}

if (newTotalSupply < 0) {
// Negative from a token transfer of a token dissociate of a deleted token, so we aggregate the change.
totalSupply = totalSupply == null ? newTotalSupply : totalSupply + newTotalSupply;
} else {
totalSupply = newTotalSupply;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,6 @@ private void insertFungibleTokenTransfers(RecordItem recordItem, TokenTransferLi
// to bring the account's balance of the token to 0. Set the totalSupply of the token object to the
// negative amount, later in the pipeline the token total supply will be reduced accordingly
Token token = new Token();
token.setTimestampLower(consensusTimestamp);
token.setTokenId(tokenId.getId());
token.setTotalSupply(accountAmount.getAmount());
entityListener.onToken(token);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -424,7 +424,9 @@ public void onStakingRewardTransfer(StakingRewardTransfer stakingRewardTransfer)
@Override
public void onToken(Token token) throws ImporterException {
var merged = tokenState.merge(token.getTokenId(), token, this::mergeToken);
tokens.add(merged);
if (merged == token) {
tokens.add(merged);
}
}

@Override
Expand Down Expand Up @@ -630,7 +632,7 @@ private ContractState mergeContractState(ContractState previous, ContractState c
@SuppressWarnings("java:S3776")
private Entity mergeEntity(Entity previous, Entity current) {
// This entity should not trigger a history record, so just copy common non-history fields, if set, to previous
if (!current.isHistory()) {
if (!current.hasHistory()) {
previous.addBalance(current.getBalance());
if (current.getBalanceTimestamp() != null) {
previous.setBalanceTimestamp(current.getBalanceTimestamp());
Expand All @@ -648,11 +650,11 @@ private Entity mergeEntity(Entity previous, Entity current) {
}

// If previous doesn't have history, merge reversely from current to previous
var src = previous.isHistory() ? previous : current;
var dest = previous.isHistory() ? current : previous;
var src = previous.hasHistory() ? previous : current;
var dest = previous.hasHistory() ? current : previous;

boolean isSameTimestampLower = Objects.equals(current.getTimestampLower(), previous.getTimestampLower());
if (current.isHistory() && isSameTimestampLower) {
if (current.hasHistory() && isSameTimestampLower) {
// Copy from current to previous if the updates have the same lower timestamp (thus from same transaction)
src = current;
dest = previous;
Expand Down Expand Up @@ -760,7 +762,7 @@ private Entity mergeEntity(Entity previous, Entity current) {
// There is at least one entity with history. If there is one without history, it must be dest and copy non-null
// fields and timestamp range from src to dest. Otherwise, both have history, and it's a normal merge from
// previous to current, so close the src entity's timestamp range
if (!dest.isHistory()) {
if (!dest.hasHistory()) {
dest.setTimestampRange(src.getTimestampRange());
// It's important to set the type since some non-history updates may have incorrect entity type.
// For example, when a contract is created in a child transaction, the initial transfer to the contract may
Expand All @@ -776,7 +778,7 @@ private Entity mergeEntity(Entity previous, Entity current) {
}

private <T extends FungibleAllowance> T mergeFungibleAllowance(T previous, T current) {
if (current.isHistory()) {
if (current.hasHistory()) {
// Current is an allowance grant / revoke so close the previous timestamp range
previous.setTimestampUpper(current.getTimestampLower());
return current;
Expand Down Expand Up @@ -852,6 +854,19 @@ private Schedule mergeSchedule(Schedule cachedSchedule, Schedule schedule) {
* @return the merged token
*/
private Token mergeToken(Token previous, Token current) {

if (!current.hasHistory()) {
previous.setTotalSupply(current.getTotalSupply());
return previous;
}

// When current has history, current should always have a null totalSupply,
// Hence, it is safe to merge total supply from previous to current here.
if (!previous.hasHistory()) {
current.setTotalSupply(previous.getTotalSupply());
return current;
}

previous.setTimestampUpper(current.getTimestampLower());

current.setCreatedTimestamp(previous.getCreatedTimestamp());
Expand Down Expand Up @@ -902,22 +917,16 @@ private Token mergeToken(Token previous, Token current) {
current.setWipeKey(previous.getWipeKey());
}

Long currentTotalSupply = current.getTotalSupply();
Long previousTotalSupply = previous.getTotalSupply();

if (currentTotalSupply == null) {
current.setTotalSupply(previousTotalSupply);
} else if (previousTotalSupply != null && currentTotalSupply < 0) {
// Negative from a token transfer of a token dissociate of a deleted token, so we aggregate the change.
current.setTotalSupply(previousTotalSupply + currentTotalSupply);
}
// This method should not be called with negative total supply since wipe/burn/token dissociate of a deleted
// token will not have history so will not reach here.
current.setTotalSupply(previous.getTotalSupply());

return current;
}

private TokenAccount mergeTokenAccount(TokenAccount lastTokenAccount, TokenAccount newTokenAccount) {
if (!lastTokenAccount.isHistory()) {
if (newTokenAccount.isHistory()) {
if (!lastTokenAccount.hasHistory()) {
if (newTokenAccount.hasHistory()) {
lastTokenAccount.setAutomaticAssociation(newTokenAccount.getAutomaticAssociation());
lastTokenAccount.setAssociated(newTokenAccount.getAssociated());
lastTokenAccount.setCreatedTimestamp(newTokenAccount.getCreatedTimestamp());
Expand All @@ -930,7 +939,7 @@ private TokenAccount mergeTokenAccount(TokenAccount lastTokenAccount, TokenAccou
return mergeTokenAccountBalance(lastTokenAccount, newTokenAccount);
}

if (lastTokenAccount.isHistory() && !newTokenAccount.isHistory()) {
if (lastTokenAccount.hasHistory() && !newTokenAccount.hasHistory()) {
return mergeTokenAccountBalance(lastTokenAccount, newTokenAccount);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ protected void doUpdateTransaction(Transaction transaction, RecordItem recordIte
long newTotalSupply = recordItem.getTransactionRecord().getReceipt().getNewTotalSupply();

var token = new Token();
token.setTimestampLower(consensusTimestamp);
token.setTotalSupply(newTotalSupply);
token.setTokenId(tokenId.getId());
entityListener.onToken(token);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@ protected void doUpdateTransaction(Transaction transaction, RecordItem recordIte
long newTotalSupply = recordItem.getTransactionRecord().getReceipt().getNewTotalSupply();

var token = new Token();
token.setTimestampLower(consensusTimestamp);
token.setTokenId(tokenId.getId());
token.setTotalSupply(newTotalSupply);
entityListener.onToken(token);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ protected void doUpdateTransaction(Transaction transaction, RecordItem recordIte
long newTotalSupply = recordItem.getTransactionRecord().getReceipt().getNewTotalSupply();

var token = new Token();
token.setTimestampLower(consensusTimestamp);
token.setTokenId(tokenId.getId());
token.setTotalSupply(newTotalSupply);
entityListener.onToken(token);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* 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.
* 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.mirror.importer.domain.token;

import static org.assertj.core.api.Assertions.assertThat;

import com.hedera.mirror.common.domain.token.Token;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;

class AbstractTokenTest {

@ParameterizedTest
@CsvSource(
textBlock =
"""
-100, , -100
, 500, 500
, -500, -500
1200, -500, 700
""")
void shouldSetTotalSupplyToNewSupply(Long totalSupply, Long newSupply, Long expected) {
// given
// totalSupply will have a null value here
var token = new Token();

// when
// This will set the initial value to totalSupply for the purpose of this test
token.setTotalSupply(totalSupply);

// This will be the updated value of totalSupply
token.setTotalSupply(newSupply);

// then
assertThat(token.getTotalSupply()).isEqualTo(expected);
}
}
Loading

0 comments on commit 22f2a8d

Please sign in to comment.