Skip to content

Commit

Permalink
feat(freezeV2): add cancel unfreezeV2 interface
Browse files Browse the repository at this point in the history
  • Loading branch information
lxcmyf committed Jun 2, 2023
1 parent dcf8982 commit 55ca979
Show file tree
Hide file tree
Showing 27 changed files with 694 additions and 34 deletions.
2 changes: 2 additions & 0 deletions Tron protobuf protocol document.md
Expand Up @@ -627,6 +627,7 @@ Transaction and transaction-related messages.
WithdrawExpireUnfreezeContract = 56;
DelegateResourceContract = 57;
UnDelegateResourceContract = 58;
CancelUnfreezeV2Contract = 59;
}
ContractType type = 1;
google.protobuf.Any parameter = 2;
Expand Down Expand Up @@ -887,6 +888,7 @@ Contract and contract-related messages.
WithdrawExpireUnfreezeContract = 56;
DelegateResourceContract = 57;
UnDelegateResourceContract = 58;
CancelUnfreezeV2Contract = 59;
}
ContractType type = 1;
google.protobuf.Any parameter = 2;
Expand Down
2 changes: 1 addition & 1 deletion actuator/build.gradle
Expand Up @@ -3,7 +3,7 @@ description = "actuator – a series of transactions for blockchain."
// Dependency versions
// ---------------------------------------

def junitVersion = "4.12"
def junitVersion = "4.13.2"
def mockitoVersion = "2.1.0"
def testNgVersion = "6.11"
def slf4jVersion = "1.7.25"
Expand Down
@@ -0,0 +1,229 @@
package org.tron.core.actuator;

import static org.tron.core.actuator.ActuatorConstant.ACCOUNT_EXCEPTION_STR;
import static org.tron.core.actuator.ActuatorConstant.NOT_EXIST_STR;
import static org.tron.core.config.Parameter.ChainConstant.TRX_PRECISION;
import static org.tron.protos.contract.Common.ResourceCode.BANDWIDTH;
import static org.tron.protos.contract.Common.ResourceCode.ENERGY;

import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.tuple.Triple;
import org.tron.common.utils.DecodeUtil;
import org.tron.common.utils.StringUtil;
import org.tron.core.capsule.AccountCapsule;
import org.tron.core.capsule.TransactionResultCapsule;
import org.tron.core.exception.ContractExeException;
import org.tron.core.exception.ContractValidateException;
import org.tron.core.store.AccountStore;
import org.tron.core.store.DynamicPropertiesStore;
import org.tron.protos.Protocol.Account.UnFreezeV2;
import org.tron.protos.Protocol.Transaction.Contract.ContractType;
import org.tron.protos.Protocol.Transaction.Result.code;
import org.tron.protos.contract.BalanceContract.CancelUnfreezeV2Contract;

@Slf4j(topic = "actuator")
public class CancelUnfreezeV2Actuator extends AbstractActuator {

public CancelUnfreezeV2Actuator() {
super(ContractType.CancelUnfreezeV2Contract, CancelUnfreezeV2Contract.class);
}

@Override
public boolean execute(Object result) throws ContractExeException {
TransactionResultCapsule ret = (TransactionResultCapsule) result;
if (Objects.isNull(ret)) {
throw new RuntimeException(ActuatorConstant.TX_RESULT_NULL);
}
long fee = calcFee();
AccountStore accountStore = chainBaseManager.getAccountStore();
DynamicPropertiesStore dynamicStore = chainBaseManager.getDynamicPropertiesStore();
final CancelUnfreezeV2Contract cancelUnfreezeV2Contract;
byte[] ownerAddress;
try {
cancelUnfreezeV2Contract = getCancelUnfreezeV2Contract();
ownerAddress = getOwnerAddress().toByteArray();
} catch (InvalidProtocolBufferException e) {
logger.debug(e.getMessage(), e);
ret.setStatus(fee, code.FAILED);
throw new ContractExeException(e.getMessage());
}
List<Integer> indexList = cancelUnfreezeV2Contract.getIndexList()
.stream().sorted().collect(Collectors.toList());
AccountCapsule ownerCapsule = accountStore.get(ownerAddress);
List<UnFreezeV2> unfrozenV2List = ownerCapsule.getUnfrozenV2List();
long now = dynamicStore.getLatestBlockHeaderTimestamp();
AtomicLong atomicWithdrawExpireBalance = new AtomicLong(0L);
AtomicLong atomicCancelBalance = new AtomicLong(0L);
Triple<AtomicLong, AtomicLong, AtomicLong> triple =
Triple.of(new AtomicLong(0L), new AtomicLong(0L), new AtomicLong(0L));
List<UnFreezeV2> newUnFreezeV2List = null;
if (indexList.isEmpty()) {
for (UnFreezeV2 unFreezeV2 : unfrozenV2List) {
updateAndCalculate(triple, ownerCapsule, now, atomicWithdrawExpireBalance,
atomicCancelBalance, unFreezeV2);
}
} else {
indexList.forEach(index -> {
UnFreezeV2 unFreezeV2 = unfrozenV2List.get(index);
updateAndCalculate(triple, ownerCapsule, now, atomicWithdrawExpireBalance,
atomicCancelBalance, unFreezeV2);
});
newUnFreezeV2List = unfrozenV2List.stream()
.filter(o -> !indexList.contains(unfrozenV2List.indexOf(o))).collect(Collectors.toList());
}
ownerCapsule.clearUnfrozenV2();
ownerCapsule.addAllUnfrozenV2(newUnFreezeV2List);
addTotalResourceWeight(dynamicStore, triple);

long withdrawExpireBalance = atomicWithdrawExpireBalance.get();
if (withdrawExpireBalance > 0) {
ownerCapsule.setBalance(ownerCapsule.getBalance() + withdrawExpireBalance);
}

accountStore.put(ownerCapsule.createDbKey(), ownerCapsule);
ret.setWithdrawExpireAmount(withdrawExpireBalance);
ret.setCancelUnfreezeV2Amount(atomicCancelBalance.get());
ret.setStatus(fee, code.SUCESS);
return true;
}

private void addTotalResourceWeight(DynamicPropertiesStore dynamicStore,
Triple<AtomicLong, AtomicLong, AtomicLong> triple) {
dynamicStore.addTotalNetWeight(triple.getLeft().get());
dynamicStore.addTotalEnergyWeight(triple.getMiddle().get());
dynamicStore.addTotalTronPowerWeight(triple.getRight().get());
}

private void updateAndCalculate(Triple<AtomicLong, AtomicLong, AtomicLong> triple,
AccountCapsule ownerCapsule, long now, AtomicLong atomicLong, AtomicLong cancelBalance,
UnFreezeV2 unFreezeV2) {
if (unFreezeV2.getUnfreezeExpireTime() > now) {
updateFrozenInfoAndTotalResourceWeight(ownerCapsule, unFreezeV2, triple);
cancelBalance.addAndGet(unFreezeV2.getUnfreezeAmount());
} else {
atomicLong.addAndGet(unFreezeV2.getUnfreezeAmount());
}
}

@Override
public boolean validate() throws ContractValidateException {
if (Objects.isNull(this.any)) {
throw new ContractValidateException(ActuatorConstant.CONTRACT_NOT_EXIST);
}

if (Objects.isNull(chainBaseManager)) {
throw new ContractValidateException(ActuatorConstant.STORE_NOT_EXIST);
}

AccountStore accountStore = chainBaseManager.getAccountStore();
DynamicPropertiesStore dynamicStore = chainBaseManager.getDynamicPropertiesStore();

if (!this.any.is(CancelUnfreezeV2Contract.class)) {
throw new ContractValidateException("contract type error, expected type " +
"[CancelUnfreezeV2Contract], real type[" + any.getClass() + "]");
}

if (!dynamicStore.supportAllowCancelUnfreezeV2()) {
throw new ContractValidateException("Not support CancelUnfreezeV2 transaction,"
+ " need to be opened by the committee");
}

final CancelUnfreezeV2Contract cancelUnfreezeV2Contract;
byte[] ownerAddress;
try {
cancelUnfreezeV2Contract = getCancelUnfreezeV2Contract();
ownerAddress = getOwnerAddress().toByteArray();
} catch (InvalidProtocolBufferException e) {
logger.debug(e.getMessage(), e);
throw new ContractValidateException(e.getMessage());
}

if (!DecodeUtil.addressValid(ownerAddress)) {
throw new ContractValidateException("Invalid address");
}
AccountCapsule accountCapsule = accountStore.get(ownerAddress);
String readableOwnerAddress = StringUtil.createReadableString(ownerAddress);
if (Objects.isNull(accountCapsule)) {
throw new ContractValidateException(ACCOUNT_EXCEPTION_STR
+ readableOwnerAddress + NOT_EXIST_STR);
}

List<UnFreezeV2> unfrozenV2List = accountCapsule.getUnfrozenV2List();
if (unfrozenV2List.isEmpty()) {
throw new ContractValidateException("No unfreezeV2 list to cancel");
}

List<Integer> indexList = cancelUnfreezeV2Contract.getIndexList();
if (indexList.size() > unfrozenV2List.size()) {
throw new ContractValidateException(
"The size[" + indexList.size() + "] of the index cannot exceed the size["
+ unfrozenV2List.size() + "] of unfreezeV2!");
}

for (Integer i : indexList) {
int maxIndex = unfrozenV2List.size() - 1;
if (i < 0 || i > maxIndex) {
throw new ContractValidateException(
"The input index[" + i + "] cannot be less than 0 and cannot be "
+ "greater than the maximum index[" + maxIndex + "] of unfreezeV2!");
}
}
Set<Integer> set = new HashSet<>();
List<Integer> dps = indexList.stream().filter(n -> !set.add(n)).collect(Collectors.toList());
if (CollectionUtils.isNotEmpty(dps)) {
throw new ContractValidateException("The element" + dps + " in the index list is duplicated");
}
return true;
}

@Override
public ByteString getOwnerAddress() throws InvalidProtocolBufferException {
return getCancelUnfreezeV2Contract().getOwnerAddress();
}

private CancelUnfreezeV2Contract getCancelUnfreezeV2Contract()
throws InvalidProtocolBufferException {
return any.unpack(CancelUnfreezeV2Contract.class);
}

@Override
public long calcFee() {
return 0;
}

public void updateFrozenInfoAndTotalResourceWeight(
AccountCapsule accountCapsule, UnFreezeV2 unFreezeV2,
Triple<AtomicLong, AtomicLong, AtomicLong> triple) {
switch (unFreezeV2.getType()) {
case BANDWIDTH:
long oldNetWeight = accountCapsule.getFrozenV2BalanceWithDelegated(BANDWIDTH) / TRX_PRECISION;
accountCapsule.addFrozenBalanceForBandwidthV2(unFreezeV2.getUnfreezeAmount());
long newNetWeight = accountCapsule.getFrozenV2BalanceWithDelegated(BANDWIDTH) / TRX_PRECISION;
triple.getLeft().addAndGet(newNetWeight - oldNetWeight);
break;
case ENERGY:
long oldEnergyWeight = accountCapsule.getFrozenV2BalanceWithDelegated(ENERGY) / TRX_PRECISION;
accountCapsule.addFrozenBalanceForEnergyV2(unFreezeV2.getUnfreezeAmount());
long newEnergyWeight = accountCapsule.getFrozenV2BalanceWithDelegated(ENERGY) / TRX_PRECISION;
triple.getMiddle().addAndGet(newEnergyWeight - oldEnergyWeight);
break;
case TRON_POWER:
long oldTPWeight = accountCapsule.getTronPowerFrozenV2Balance() / TRX_PRECISION;
accountCapsule.addFrozenForTronPowerV2(unFreezeV2.getUnfreezeAmount());
long newTPWeight = accountCapsule.getTronPowerFrozenV2Balance() / TRX_PRECISION;
triple.getRight().addAndGet(newTPWeight - oldTPWeight);
break;
default:
break;
}
}
}
19 changes: 18 additions & 1 deletion actuator/src/main/java/org/tron/core/utils/ProposalUtil.java
Expand Up @@ -692,6 +692,22 @@ public static void validator(DynamicPropertiesStore dynamicPropertiesStore,
}
break;
}
case ALLOW_CANCEL_UNFREEZE_V2: {
if (!forkController.pass(ForkBlockVersionEnum.VERSION_4_7_2)) {
throw new ContractValidateException(
"Bad chain parameter id [ALLOW_CANCEL_UNFREEZE_V2]");
}
if (value != 1) {
throw new ContractValidateException(
"This value[ALLOW_CANCEL_UNFREEZE_V2] is only allowed to be 1");
}
if (dynamicPropertiesStore.getUnfreezeDelayDays() == 0) {
throw new ContractValidateException(
"[UNFREEZE_DELAY_DAYS] proposal must be approved "
+ "before [ALLOW_CANCEL_UNFREEZE_V2] can be proposed");
}
break;
}
default:
break;
}
Expand Down Expand Up @@ -765,7 +781,8 @@ public enum ProposalType { // current value, value range
DYNAMIC_ENERGY_THRESHOLD(73), // 0, [0, LONG]
DYNAMIC_ENERGY_INCREASE_FACTOR(74), // 0, [0, 10_000]
DYNAMIC_ENERGY_MAX_FACTOR(75), // 0, [0, 100_000]
ALLOW_TVM_SHANGHAI(76); // 0, 1
ALLOW_TVM_SHANGHAI(76), // 0, 1
ALLOW_CANCEL_UNFREEZE_V2(77); // 0, 1

private long code;

Expand Down
2 changes: 1 addition & 1 deletion chainbase/build.gradle
Expand Up @@ -3,7 +3,7 @@ description = "chainbase – a decentralized database for blockchain."
// Dependency versions
// ---------------------------------------

def junitVersion = "4.12"
def junitVersion = "4.13.2"
def mockitoVersion = "2.1.0"
def testNgVersion = "6.11"
def jacocoVersion = "0.8.0"
Expand Down
Expand Up @@ -89,6 +89,15 @@ public void setWithdrawExpireAmount(long amount) {
.setWithdrawExpireAmount(amount).build();
}

public long getCancelUnfreezeV2Amount() {
return transactionResult.getCancelUnfreezeV2Amount();
}

public void setCancelUnfreezeV2Amount(long amount) {
this.transactionResult = this.transactionResult.toBuilder()
.setCancelUnfreezeV2Amount(amount).build();
}

public long getExchangeReceivedAmount() {
return transactionResult.getExchangeReceivedAmount();
}
Expand Down
Expand Up @@ -100,6 +100,7 @@ public static TransactionInfoCapsule buildTransactionInfoInstance(TransactionCap
builder.setExchangeId(programResult.getRet().getExchangeId());
builder.setWithdrawAmount(programResult.getRet().getWithdrawAmount());
builder.setWithdrawExpireAmount(programResult.getRet().getWithdrawExpireAmount());
builder.setCancelUnfreezeV2Amount(programResult.getRet().getCancelUnfreezeV2Amount());
builder.setExchangeReceivedAmount(programResult.getRet().getExchangeReceivedAmount());
builder.setExchangeInjectAnotherAmount(programResult.getRet().getExchangeInjectAnotherAmount());
builder.setExchangeWithdrawAnotherAmount(
Expand Down

0 comments on commit 55ca979

Please sign in to comment.