diff --git a/Tron protobuf protocol document.md b/Tron protobuf protocol document.md index f6fa2219ce3..0c027ea8dd8 100644 --- a/Tron protobuf protocol document.md +++ b/Tron protobuf protocol document.md @@ -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; @@ -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; diff --git a/actuator/build.gradle b/actuator/build.gradle index 3db2e55b3de..a230ef4ca7e 100644 --- a/actuator/build.gradle +++ b/actuator/build.gradle @@ -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" diff --git a/actuator/src/main/java/org/tron/core/actuator/CancelUnfreezeV2Actuator.java b/actuator/src/main/java/org/tron/core/actuator/CancelUnfreezeV2Actuator.java new file mode 100755 index 00000000000..aa0ff2fe64e --- /dev/null +++ b/actuator/src/main/java/org/tron/core/actuator/CancelUnfreezeV2Actuator.java @@ -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 indexList = cancelUnfreezeV2Contract.getIndexList() + .stream().sorted().collect(Collectors.toList()); + AccountCapsule ownerCapsule = accountStore.get(ownerAddress); + List unfrozenV2List = ownerCapsule.getUnfrozenV2List(); + long now = dynamicStore.getLatestBlockHeaderTimestamp(); + AtomicLong atomicWithdrawExpireBalance = new AtomicLong(0L); + AtomicLong atomicCancelBalance = new AtomicLong(0L); + Triple triple = + Triple.of(new AtomicLong(0L), new AtomicLong(0L), new AtomicLong(0L)); + List 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 triple) { + dynamicStore.addTotalNetWeight(triple.getLeft().get()); + dynamicStore.addTotalEnergyWeight(triple.getMiddle().get()); + dynamicStore.addTotalTronPowerWeight(triple.getRight().get()); + } + + private void updateAndCalculate(Triple 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 unfrozenV2List = accountCapsule.getUnfrozenV2List(); + if (unfrozenV2List.isEmpty()) { + throw new ContractValidateException("No unfreezeV2 list to cancel"); + } + + List 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 set = new HashSet<>(); + List 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 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; + } + } +} diff --git a/actuator/src/main/java/org/tron/core/utils/ProposalUtil.java b/actuator/src/main/java/org/tron/core/utils/ProposalUtil.java index 9ab68cc1925..1f957d3324d 100644 --- a/actuator/src/main/java/org/tron/core/utils/ProposalUtil.java +++ b/actuator/src/main/java/org/tron/core/utils/ProposalUtil.java @@ -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; } @@ -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; diff --git a/chainbase/build.gradle b/chainbase/build.gradle index cac7efc6fa5..ed9911b0032 100644 --- a/chainbase/build.gradle +++ b/chainbase/build.gradle @@ -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" diff --git a/chainbase/src/main/java/org/tron/core/capsule/TransactionResultCapsule.java b/chainbase/src/main/java/org/tron/core/capsule/TransactionResultCapsule.java index 98bb23c4729..4e93a52d2ac 100644 --- a/chainbase/src/main/java/org/tron/core/capsule/TransactionResultCapsule.java +++ b/chainbase/src/main/java/org/tron/core/capsule/TransactionResultCapsule.java @@ -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(); } diff --git a/chainbase/src/main/java/org/tron/core/capsule/utils/TransactionUtil.java b/chainbase/src/main/java/org/tron/core/capsule/utils/TransactionUtil.java index 2d438229ee6..ca366a3d8d7 100644 --- a/chainbase/src/main/java/org/tron/core/capsule/utils/TransactionUtil.java +++ b/chainbase/src/main/java/org/tron/core/capsule/utils/TransactionUtil.java @@ -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( diff --git a/chainbase/src/main/java/org/tron/core/store/DynamicPropertiesStore.java b/chainbase/src/main/java/org/tron/core/store/DynamicPropertiesStore.java index 001282a0de8..5138691d226 100644 --- a/chainbase/src/main/java/org/tron/core/store/DynamicPropertiesStore.java +++ b/chainbase/src/main/java/org/tron/core/store/DynamicPropertiesStore.java @@ -14,7 +14,6 @@ import org.tron.common.utils.ByteArray; import org.tron.common.utils.Sha256Hash; import org.tron.core.capsule.BytesCapsule; -import org.tron.core.config.Parameter; import org.tron.core.config.Parameter.ChainConstant; import org.tron.core.db.TronStoreWithRevoking; import org.tron.core.exception.BadItemException; @@ -208,6 +207,9 @@ public class DynamicPropertiesStore extends TronStoreWithRevoking private static final byte[] ALLOW_TVM_SHANGHAI = "ALLOW_TVM_SHANGHAI".getBytes(); + private static final byte[] ALLOW_CANCEL_UNFREEZE_V2 = "ALLOW_CANCEL_UNFREEZE_V2" + .getBytes(); + @Autowired private DynamicPropertiesStore(@Value("properties") String dbName) { super(dbName); @@ -2192,7 +2194,7 @@ public long getNextMaintenanceTime() { } public long getMaintenanceSkipSlots() { - return Parameter.ChainConstant.MAINTENANCE_SKIP_SLOTS; + return ChainConstant.MAINTENANCE_SKIP_SLOTS; } public void saveNextMaintenanceTime(long nextMaintenanceTime) { @@ -2218,6 +2220,9 @@ public void updateNextMaintenanceTime(long blockTime) { //The unit is trx public void addTotalNetWeight(long amount) { + if (amount == 0) { + return; + } long totalNetWeight = getTotalNetWeight(); totalNetWeight += amount; if (allowNewReward()) { @@ -2228,6 +2233,9 @@ public void addTotalNetWeight(long amount) { //The unit is trx public void addTotalEnergyWeight(long amount) { + if (amount == 0) { + return; + } long totalEnergyWeight = getTotalEnergyWeight(); totalEnergyWeight += amount; if (allowNewReward()) { @@ -2238,6 +2246,9 @@ public void addTotalEnergyWeight(long amount) { //The unit is trx public void addTotalTronPowerWeight(long amount) { + if (amount == 0) { + return; + } long totalWeight = getTotalTronPowerWeight(); totalWeight += amount; if (allowNewReward()) { @@ -2769,6 +2780,22 @@ public long getAllowTvmShangHai() { .orElse(CommonParameter.getInstance().getAllowTvmShangHai()); } + public void saveAllowCancelUnfreezeV2(long allowCancelUnfreezeV2) { + this.put(DynamicPropertiesStore.ALLOW_CANCEL_UNFREEZE_V2, + new BytesCapsule(ByteArray.fromLong(allowCancelUnfreezeV2))); + } + + public long getAllowCancelUnfreezeV2() { + return Optional.ofNullable(getUnchecked(ALLOW_CANCEL_UNFREEZE_V2)) + .map(BytesCapsule::getData) + .map(ByteArray::toLong) + .orElse(CommonParameter.getInstance().getAllowCancelUnfreezeV2()); + } + + public boolean supportAllowCancelUnfreezeV2() { + return getAllowCancelUnfreezeV2() == 1L && getUnfreezeDelayDays() > 0; + } + private static class DynamicResourceProperties { private static final byte[] ONE_DAY_NET_LIMIT = "ONE_DAY_NET_LIMIT".getBytes(); diff --git a/common/build.gradle b/common/build.gradle index d065d3cbf89..95c6312ebcd 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -35,7 +35,7 @@ repositories { } dependencies { - testCompile group: 'junit', name: 'junit', version: '4.12' + testCompile group: 'junit', name: 'junit', version: '4.13.2' compile group: 'org.bouncycastle', name: 'bcprov-jdk15on', version: '1.69' compile group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.13.4.1' compile "com.cedarsoftware:java-util:1.8.0" diff --git a/common/src/main/java/org/tron/common/parameter/CommonParameter.java b/common/src/main/java/org/tron/common/parameter/CommonParameter.java index ef89e01925a..d25f3b29600 100644 --- a/common/src/main/java/org/tron/common/parameter/CommonParameter.java +++ b/common/src/main/java/org/tron/common/parameter/CommonParameter.java @@ -651,6 +651,10 @@ public class CommonParameter { @Setter public long allowTvmShangHai; + @Getter + @Setter + public long allowCancelUnfreezeV2; + private static double calcMaxTimeRatio() { //return max(2.0, min(5.0, 5 * 4.0 / max(Runtime.getRuntime().availableProcessors(), 1))); return 5.0; diff --git a/consensus/build.gradle b/consensus/build.gradle index c2393064d46..c22e961572e 100644 --- a/consensus/build.gradle +++ b/consensus/build.gradle @@ -2,7 +2,7 @@ description = "consensus – a distributed consensus arithmetic 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" diff --git a/crypto/build.gradle b/crypto/build.gradle index 9d90368814d..19ae8e805fe 100644 --- a/crypto/build.gradle +++ b/crypto/build.gradle @@ -11,7 +11,7 @@ repositories { } dependencies { - testCompile group: 'junit', name: 'junit', version: '4.12' + testCompile group: 'junit', name: 'junit', version: '4.13.2' compile group: 'org.bouncycastle', name: 'bcprov-jdk15on', version: '1.69' compile project(":common") } diff --git a/framework/build.gradle b/framework/build.gradle index 6f6f861a6e9..0451ece2e64 100644 --- a/framework/build.gradle +++ b/framework/build.gradle @@ -40,7 +40,7 @@ dependencies { //local libraries compile fileTree(dir: 'libs', include: '*.jar') // end local libraries - testCompile group: 'junit', name: 'junit', version: '4.12' + testCompile group: 'junit', name: 'junit', version: '4.13.2' testCompile group: 'org.mockito', name: 'mockito-core', version: '2.13.0' testCompile group: 'org.hamcrest', name: 'hamcrest-junit', version: '1.0.0.1' @@ -54,7 +54,7 @@ dependencies { compile group: 'com.beust', name: 'jcommander', version: '1.72' - compile group: 'junit', name: 'junit', version: '4.12' + compile group: 'junit', name: 'junit', version: '4.13.2' compile group: 'net.jcip', name: 'jcip-annotations', version: '1.0' diff --git a/framework/src/main/java/org/tron/core/Wallet.java b/framework/src/main/java/org/tron/core/Wallet.java index a836b15d59d..c3f15099648 100755 --- a/framework/src/main/java/org/tron/core/Wallet.java +++ b/framework/src/main/java/org/tron/core/Wallet.java @@ -1325,6 +1325,11 @@ public Protocol.ChainParameters getChainParameters() { .setValue(dbManager.getDynamicPropertiesStore().getAllowTvmShangHai()) .build()); + builder.addChainParameter(Protocol.ChainParameters.ChainParameter.newBuilder() + .setKey("getAllowCancelUnfreezeV2") + .setValue(dbManager.getDynamicPropertiesStore().getAllowCancelUnfreezeV2()) + .build()); + return builder.build(); } diff --git a/framework/src/main/java/org/tron/core/consensus/ProposalService.java b/framework/src/main/java/org/tron/core/consensus/ProposalService.java index 4ca90eae640..19c766f91dd 100644 --- a/framework/src/main/java/org/tron/core/consensus/ProposalService.java +++ b/framework/src/main/java/org/tron/core/consensus/ProposalService.java @@ -343,6 +343,14 @@ public static boolean process(Manager manager, ProposalCapsule proposalCapsule) manager.getDynamicPropertiesStore().saveAllowTvmShangHai(entry.getValue()); break; } + case ALLOW_CANCEL_UNFREEZE_V2: { + if (manager.getDynamicPropertiesStore().getAllowCancelUnfreezeV2() == 0) { + manager.getDynamicPropertiesStore().saveAllowCancelUnfreezeV2(entry.getValue()); + manager.getDynamicPropertiesStore().addSystemContractAndSetPermission( + ContractType.CancelUnfreezeV2Contract_VALUE); + } + break; + } default: find = false; break; diff --git a/framework/src/main/java/org/tron/core/services/RpcApiService.java b/framework/src/main/java/org/tron/core/services/RpcApiService.java index 1f767eeb237..e99ed96dd46 100755 --- a/framework/src/main/java/org/tron/core/services/RpcApiService.java +++ b/framework/src/main/java/org/tron/core/services/RpcApiService.java @@ -130,6 +130,7 @@ import org.tron.protos.contract.BalanceContract.AccountBalanceRequest; import org.tron.protos.contract.BalanceContract.AccountBalanceResponse; import org.tron.protos.contract.BalanceContract.BlockBalanceTrace; +import org.tron.protos.contract.BalanceContract.CancelUnfreezeV2Contract; import org.tron.protos.contract.BalanceContract.DelegateResourceContract; import org.tron.protos.contract.BalanceContract.FreezeBalanceContract; import org.tron.protos.contract.BalanceContract.TransferContract; @@ -1471,6 +1472,13 @@ public void unDelegateResource(UnDelegateResourceContract request, responseObserver); } + @Override + public void cancelUnfreezeV2(CancelUnfreezeV2Contract request, + StreamObserver responseObserver) { + createTransactionExtention(request, ContractType.CancelUnfreezeV2Contract, + responseObserver); + } + @Override public void proposalCreate(ProposalCreateContract request, StreamObserver responseObserver) { diff --git a/framework/src/main/java/org/tron/core/services/http/CancelUnfreezeV2Servlet.java b/framework/src/main/java/org/tron/core/services/http/CancelUnfreezeV2Servlet.java new file mode 100644 index 00000000000..601379e5547 --- /dev/null +++ b/framework/src/main/java/org/tron/core/services/http/CancelUnfreezeV2Servlet.java @@ -0,0 +1,38 @@ +package org.tron.core.services.http; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.tron.core.Wallet; +import org.tron.protos.Protocol.Transaction; +import org.tron.protos.Protocol.Transaction.Contract.ContractType; +import org.tron.protos.contract.BalanceContract.CancelUnfreezeV2Contract; + +@Component +@Slf4j(topic = "API") +public class CancelUnfreezeV2Servlet extends RateLimiterServlet { + + @Autowired + private Wallet wallet; + + @Override + protected void doPost(HttpServletRequest request, HttpServletResponse response) { + try { + PostParams params = PostParams.getPostParams(request); + CancelUnfreezeV2Contract.Builder build = CancelUnfreezeV2Contract.newBuilder(); + JsonFormat.merge(params.getParams(), build, params.isVisible()); + Transaction tx = wallet + .createTransactionCapsule(build.build(), ContractType.CancelUnfreezeV2Contract) + .getInstance(); + JSONObject jsonObject = JSON.parseObject(params.getParams()); + tx = Util.setTransactionPermissionId(jsonObject, tx); + response.getWriter().println(Util.printCreateTransaction(tx, params.isVisible())); + } catch (Exception e) { + Util.processError(e, response); + } + } +} diff --git a/framework/src/main/java/org/tron/core/services/http/FullNodeHttpApiService.java b/framework/src/main/java/org/tron/core/services/http/FullNodeHttpApiService.java index 030adc489ed..cac81174939 100644 --- a/framework/src/main/java/org/tron/core/services/http/FullNodeHttpApiService.java +++ b/framework/src/main/java/org/tron/core/services/http/FullNodeHttpApiService.java @@ -302,6 +302,8 @@ public class FullNodeHttpApiService implements Service { private DelegateResourceServlet delegateResourceServlet; @Autowired private UnDelegateResourceServlet unDelegateResourceServlet; + @Autowired + private CancelUnfreezeV2Servlet cancelUnfreezeV2Servlet; private static String getParamsFile(String fileName) { InputStream in = Thread.currentThread().getContextClassLoader() @@ -564,6 +566,8 @@ public void start() { "/wallet/delegateresource"); context.addServlet(new ServletHolder(unDelegateResourceServlet), "/wallet/undelegateresource"); + context.addServlet(new ServletHolder(cancelUnfreezeV2Servlet), + "/wallet/cancelunfreezev2"); int maxHttpConnectNumber = Args.getInstance().getMaxHttpConnectNumber(); if (maxHttpConnectNumber > 0) { diff --git a/framework/src/main/java/org/tron/core/services/http/HttpSelfFormatFieldName.java b/framework/src/main/java/org/tron/core/services/http/HttpSelfFormatFieldName.java index 93b726bb0e5..5445a387278 100644 --- a/framework/src/main/java/org/tron/core/services/http/HttpSelfFormatFieldName.java +++ b/framework/src/main/java/org/tron/core/services/http/HttpSelfFormatFieldName.java @@ -144,6 +144,8 @@ public class HttpSelfFormatFieldName { //UnDelegateResourceContract AddressFieldNameMap.put("protocol.UnDelegateResourceContract.owner_address", 1); AddressFieldNameMap.put("protocol.UnDelegateResourceContract.receiver_address", 1); + //CancelUnfreezeV2Contract + AddressFieldNameMap.put("protocol.CancelUnfreezeV2Contract.owner_address", 1); AddressFieldNameMap.put("protocol.CanDelegatedMaxSizeRequestMessage.owner_address", 1); AddressFieldNameMap.put("protocol.GetAvailableUnfreezeCountRequestMessage.owner_address", 1); AddressFieldNameMap.put("protocol.CanWithdrawUnfreezeAmountRequestMessage.owner_address", 1); diff --git a/framework/src/main/java/org/tron/core/services/jsonrpc/JsonRpcApiUtil.java b/framework/src/main/java/org/tron/core/services/jsonrpc/JsonRpcApiUtil.java index 00a33f21bff..7b71f354293 100644 --- a/framework/src/main/java/org/tron/core/services/jsonrpc/JsonRpcApiUtil.java +++ b/framework/src/main/java/org/tron/core/services/jsonrpc/JsonRpcApiUtil.java @@ -334,6 +334,9 @@ public static long getAmountFromTransactionInfo(String hash, ContractType contra case WithdrawExpireUnfreezeContract: amount = transactionInfo.getWithdrawExpireAmount(); break; + case CancelUnfreezeV2Contract: + amount = transactionInfo.getCancelUnfreezeV2Amount(); + break; default: break; } diff --git a/framework/src/test/java/org/tron/core/actuator/AccountPermissionUpdateActuatorTest.java b/framework/src/test/java/org/tron/core/actuator/AccountPermissionUpdateActuatorTest.java index 2b98557e957..888e13f1ae6 100644 --- a/framework/src/test/java/org/tron/core/actuator/AccountPermissionUpdateActuatorTest.java +++ b/framework/src/test/java/org/tron/core/actuator/AccountPermissionUpdateActuatorTest.java @@ -1,5 +1,6 @@ package org.tron.core.actuator; +import static org.testng.Assert.assertNotNull; import static org.testng.Assert.fail; import com.google.protobuf.Any; @@ -165,27 +166,23 @@ private void processAndCheckInvalid(AccountPermissionUpdateActuator actuator, actuator.execute(ret); fail(failMsg); - } catch (ContractValidateException e) { - Assert.assertTrue(e instanceof ContractValidateException); + } catch (ContractValidateException | RuntimeException e) { + Assert.assertTrue(true); Assert.assertEquals(expectedMsg, e.getMessage()); } catch (ContractExeException e) { - Assert.assertFalse(e instanceof ContractExeException); - } catch (RuntimeException e) { - Assert.assertTrue(e instanceof RuntimeException); - Assert.assertEquals(expectedMsg, e.getMessage()); + Assert.fail(); } } @Test public void successUpdatePermissionKey() { - String ownerAddress = OWNER_ADDRESS; String keyAddress = KEY_ADDRESS; // step 1, init addDefaultPermission(); // step2, check init data - byte[] owner_name_array = ByteArray.fromHexString(ownerAddress); + byte[] owner_name_array = ByteArray.fromHexString(OWNER_ADDRESS); ByteString address = ByteString.copyFrom(owner_name_array); AccountCapsule owner = dbManager.getAccountStore().get(owner_name_array); @@ -193,7 +190,7 @@ public void successUpdatePermissionKey() { Permission activePermission = AccountCapsule.createDefaultActivePermission(address, dbManager.getDynamicPropertiesStore()); - Assert.assertEquals(owner.getInstance().getActivePermissionCount(), 1); + Assert.assertEquals(1, owner.getInstance().getActivePermissionCount()); Permission ownerPermission1 = owner.getInstance().getOwnerPermission(); Permission activePermission1 = owner.getInstance().getActivePermission(0); @@ -240,21 +237,19 @@ public void successUpdatePermissionKey() { try { actuator.validate(); actuator.execute(ret); - Assert.assertEquals(ret.getInstance().getRet(), code.SUCESS); + Assert.assertEquals(code.SUCESS, ret.getInstance().getRet()); // step 4, check result after update operation owner = dbManager.getAccountStore().get(owner_name_array); - Assert.assertEquals(owner.getInstance().getActivePermissionCount(), 1); + Assert.assertEquals(1, owner.getInstance().getActivePermissionCount()); ownerPermission1 = owner.getInstance().getOwnerPermission(); activePermission1 = owner.getInstance().getActivePermission(0); Assert.assertEquals(ownerPermission1, ownerPermission); Assert.assertEquals(activePermission1, activePermission); - } catch (ContractValidateException e) { - Assert.assertFalse(e instanceof ContractValidateException); - } catch (ContractExeException e) { - Assert.assertFalse(e instanceof ContractExeException); + } catch (ContractValidateException | ContractExeException e) { + Assert.fail(); } } @@ -295,8 +290,7 @@ public void invalidTransactionResultCapsule() { AccountPermissionUpdateActuator actuator = new AccountPermissionUpdateActuator(); actuator.setChainBaseManager(dbManager.getChainBaseManager()) .setAny(getContract(OWNER_ADDRESS)); - TransactionResultCapsule ret = null; - processAndCheckInvalid(actuator, ret, "TransactionResultCapsule is null", + processAndCheckInvalid(actuator, null, "TransactionResultCapsule is null", "TransactionResultCapsule is null"); } @@ -364,7 +358,7 @@ public void activeToMany() { for (int i = 0; i <= 8; i++) { activeList.add(activePermission); } - + assertNotNull(activeList); AccountPermissionUpdateActuator actuator = new AccountPermissionUpdateActuator(); actuator.setChainBaseManager(dbManager.getChainBaseManager()) .setAny(getContract(address, ownerPermission, null, null)); @@ -930,12 +924,12 @@ public void checkAvailableContractTypeCorrespondingToCode() { // 7fff1fc0037e0000000000000000000000000000000000000000000000000000, // and it should call the addSystemContractAndSetPermission to add new contract // type - // When you add a new contact, you can add its to contractType, + // When you add a new contact, you can add it to contractType, // as '|| contractType = ContractType.XXX', // and you will get the value from the output, // then update the value to checkAvailableContractType // and checkActiveDefaultOperations - String validContractType = "7fff1fc0037ef807000000000000000000000000000000000000000000000000"; + String validContractType = "7fff1fc0037ef80f000000000000000000000000000000000000000000000000"; byte[] availableContractType = new byte[32]; for (ContractType contractType : ContractType.values()) { @@ -962,7 +956,7 @@ public void checkActiveDefaultOperationsCorrespondingToCode() { // 7fff1fc0033e0000000000000000000000000000000000000000000000000000, // and it should call the addSystemContractAndSetPermission to add new contract // type - String validContractType = "7fff1fc0033ef807000000000000000000000000000000000000000000000000"; + String validContractType = "7fff1fc0033ef80f000000000000000000000000000000000000000000000000"; byte[] availableContractType = new byte[32]; for (ContractType contractType : ContractType.values()) { @@ -985,7 +979,7 @@ public void checkActiveDefaultOperationsCorrespondingToCode() { @Test public void checkAvailableContractType() { - String validContractType = "7fff1fc0037ef907000000000000000000000000000000000000000000000000"; + String validContractType = "7fff1fc0037ef90f000000000000000000000000000000000000000000000000"; byte[] availableContractType = new byte[32]; for (ContractType contractType : ContractType.values()) { @@ -1006,7 +1000,7 @@ public void checkAvailableContractType() { @Test public void checkActiveDefaultOperations() { - String validContractType = "7fff1fc0033ef907000000000000000000000000000000000000000000000000"; + String validContractType = "7fff1fc0033ef90f000000000000000000000000000000000000000000000000"; byte[] availableContractType = new byte[32]; for (ContractType contractType : ContractType.values()) { diff --git a/framework/src/test/java/org/tron/core/actuator/CancelUnfreezeV2ActuatorTest.java b/framework/src/test/java/org/tron/core/actuator/CancelUnfreezeV2ActuatorTest.java new file mode 100644 index 00000000000..9af9c42addf --- /dev/null +++ b/framework/src/test/java/org/tron/core/actuator/CancelUnfreezeV2ActuatorTest.java @@ -0,0 +1,295 @@ +package org.tron.core.actuator; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.fail; +import static org.tron.protos.Protocol.Transaction.Result.code.SUCESS; +import static org.tron.protos.contract.Common.ResourceCode.BANDWIDTH; +import static org.tron.protos.contract.Common.ResourceCode.ENERGY; + +import com.beust.jcommander.internal.Lists; +import com.google.protobuf.Any; +import com.google.protobuf.ByteString; +import lombok.extern.slf4j.Slf4j; +import org.junit.Before; +import org.junit.Test; +import org.tron.common.BaseTest; +import org.tron.common.utils.ByteArray; +import org.tron.core.Constant; +import org.tron.core.Wallet; +import org.tron.core.capsule.AccountCapsule; +import org.tron.core.capsule.TransactionResultCapsule; +import org.tron.core.config.args.Args; +import org.tron.core.exception.ContractExeException; +import org.tron.core.exception.ContractValidateException; +import org.tron.protos.Protocol; +import org.tron.protos.contract.BalanceContract; + +@Slf4j +public class CancelUnfreezeV2ActuatorTest extends BaseTest { + + private static final String OWNER_ADDRESS; + private static final String RECEIVER_ADDRESS; + private static final String OWNER_ADDRESS_INVALID = "aaaa"; + private static final String OWNER_ACCOUNT_INVALID; + private static final long initBalance = 10_000_000_000L; + + static { + dbPath = "output_cancel_all_unfreeze_v2_test"; + Args.setParam(new String[]{"--output-directory", dbPath}, Constant.TEST_CONF); + OWNER_ADDRESS = Wallet.getAddressPreFixString() + "548794500882809695a8a687866e76d4271a1abc"; + RECEIVER_ADDRESS = Wallet.getAddressPreFixString() + "abd4b9367799eaa3197fecb144eb71de1e049150"; + OWNER_ACCOUNT_INVALID = + Wallet.getAddressPreFixString() + "548794500882809695a8a687866e76d4271a3456"; + } + + @Before + public void setUp() { + dbManager.getDynamicPropertiesStore().saveUnfreezeDelayDays(1L); + dbManager.getDynamicPropertiesStore().saveAllowCancelUnfreezeV2(1); + + AccountCapsule ownerCapsule = new AccountCapsule(ByteString.copyFromUtf8("owner"), + ByteString.copyFrom(ByteArray.fromHexString(OWNER_ADDRESS)), Protocol.AccountType.Normal, + initBalance); + dbManager.getAccountStore().put(ownerCapsule.createDbKey(), ownerCapsule); + + AccountCapsule receiverCapsule = new AccountCapsule(ByteString.copyFromUtf8("receiver"), + ByteString.copyFrom(ByteArray.fromHexString(RECEIVER_ADDRESS)), Protocol.AccountType.Normal, + initBalance); + dbManager.getAccountStore().put(receiverCapsule.getAddress().toByteArray(), receiverCapsule); + } + + @Test + public void testCancelUnfreezeV2() { + long now = System.currentTimeMillis(); + AccountCapsule accountCapsule = dbManager.getAccountStore() + .get(ByteArray.fromHexString(OWNER_ADDRESS)); + accountCapsule.addUnfrozenV2List(BANDWIDTH, 1000000L, now + 14 * 24 * 3600 * 1000); + accountCapsule.addUnfrozenV2List(ENERGY, 2000000L, -1); + accountCapsule.addUnfrozenV2List(BANDWIDTH, 3000000L, now + 14 * 24 * 3600 * 1000); + accountCapsule.addUnfrozenV2List(ENERGY, 4000000L, now + 14 * 24 * 3600 * 1000); + accountCapsule.addUnfrozenV2List(BANDWIDTH, 4000000L, 100); + accountCapsule.addUnfrozenV2List(ENERGY, 4000000L, 100); + dbManager.getAccountStore().put(accountCapsule.createDbKey(), accountCapsule); + CancelUnfreezeV2Actuator actuator = new CancelUnfreezeV2Actuator(); + actuator.setChainBaseManager(dbManager.getChainBaseManager()) + .setAny(getCancelUnfreezeV2Contract()); + TransactionResultCapsule ret = new TransactionResultCapsule(); + try { + actuator.validate(); + actuator.execute(ret); + assertEquals(SUCESS, ret.getInstance().getRet()); + AccountCapsule owner = dbManager.getAccountStore() + .get(ByteArray.fromHexString(OWNER_ADDRESS)); + assertEquals(2000000L, ret.getInstance().getWithdrawExpireAmount()); + assertEquals(2, owner.getUnfrozenV2List().size()); + } catch (ContractValidateException | ContractExeException e) { + fail(); + } + } + + @Test + public void testCancelAllUnfreezeV2() { + long now = System.currentTimeMillis(); + AccountCapsule accountCapsule = dbManager.getAccountStore() + .get(ByteArray.fromHexString(OWNER_ADDRESS)); + accountCapsule.addUnfrozenV2List(BANDWIDTH, 1000000L, now + 14 * 24 * 3600 * 1000); + accountCapsule.addUnfrozenV2List(ENERGY, 2000000L, -1); + accountCapsule.addUnfrozenV2List(BANDWIDTH, 3000000L, now + 14 * 24 * 3600 * 1000); + accountCapsule.addUnfrozenV2List(ENERGY, 4000000L, now + 14 * 24 * 3600 * 1000); + accountCapsule.addUnfrozenV2List(BANDWIDTH, 4000000L, 100); + accountCapsule.addUnfrozenV2List(ENERGY, 4000000L, 100); + dbManager.getAccountStore().put(accountCapsule.createDbKey(), accountCapsule); + CancelUnfreezeV2Actuator actuator = new CancelUnfreezeV2Actuator(); + actuator.setChainBaseManager(dbManager.getChainBaseManager()) + .setAny(getCancelAllUnfreezeV2Contract()); + TransactionResultCapsule ret = new TransactionResultCapsule(); + try { + actuator.validate(); + actuator.execute(ret); + + assertEquals(SUCESS, ret.getInstance().getRet()); + AccountCapsule owner = dbManager.getAccountStore() + .get(ByteArray.fromHexString(OWNER_ADDRESS)); + assertEquals(2000000L, ret.getInstance().getWithdrawExpireAmount()); + assertEquals(0, owner.getUnfrozenV2List().size()); + } catch (ContractValidateException | ContractExeException e) { + fail(); + } + } + + @Test + public void testNullTransactionResultCapsule() { + long now = System.currentTimeMillis(); + AccountCapsule accountCapsule = dbManager.getAccountStore() + .get(ByteArray.fromHexString(OWNER_ADDRESS)); + accountCapsule.addUnfrozenV2List(BANDWIDTH, 1000000L, now + 14 * 24 * 3600 * 1000); + accountCapsule.addUnfrozenV2List(ENERGY, 2000000L, -1); + accountCapsule.addUnfrozenV2List(BANDWIDTH, 3000000L, now + 14 * 24 * 3600 * 1000); + accountCapsule.addUnfrozenV2List(ENERGY, 4000000L, now + 14 * 24 * 3600 * 1000); + accountCapsule.addUnfrozenV2List(BANDWIDTH, 4000000L, 100); + accountCapsule.addUnfrozenV2List(ENERGY, 4000000L, 100); + dbManager.getAccountStore().put(accountCapsule.createDbKey(), accountCapsule); + CancelUnfreezeV2Actuator actuator = new CancelUnfreezeV2Actuator(); + actuator.setChainBaseManager(dbManager.getChainBaseManager()) + .setAny(getCancelUnfreezeV2Contract()); + try { + actuator.validate(); + } catch (ContractValidateException e) { + fail(); + } + assertThrows(ActuatorConstant.TX_RESULT_NULL, + RuntimeException.class, () -> actuator.execute(null)); + } + + @Test + public void testInvalidOwnerAddress() { + CancelUnfreezeV2Actuator actuator = new CancelUnfreezeV2Actuator(); + actuator.setChainBaseManager(dbManager.getChainBaseManager()) + .setAny(getCancelUnfreezeV2ContractInvalidAddress()); + assertThrows("Invalid address", ContractValidateException.class, actuator::validate); + } + + @Test + public void testInvalidOwnerAccount() { + CancelUnfreezeV2Actuator actuator = new CancelUnfreezeV2Actuator(); + actuator.setChainBaseManager(dbManager.getChainBaseManager()) + .setAny(getCancelUnfreezeV2ContractInvalidAccount()); + assertThrows("Account[" + OWNER_ACCOUNT_INVALID + "] does not exist", + ContractValidateException.class, actuator::validate); + } + + @Test + public void testInvalidOwnerUnfreezeV2List() { + CancelUnfreezeV2Actuator actuator = new CancelUnfreezeV2Actuator(); + actuator.setChainBaseManager(dbManager.getChainBaseManager()) + .setAny(getCancelUnfreezeV2Contract()); + assertThrows("no unfreezeV2 list to cancel", + ContractValidateException.class, actuator::validate); + } + + @Test + public void testInvalidCancelUnfreezeV2Contract() { + CancelUnfreezeV2Actuator actuator = new CancelUnfreezeV2Actuator(); + actuator.setChainBaseManager(dbManager.getChainBaseManager()).setAny(null); + assertThrows(ActuatorConstant.CONTRACT_NOT_EXIST, + ContractValidateException.class, actuator::validate); + } + + @Test + public void testInvalidAccountStore() { + CancelUnfreezeV2Actuator actuator = new CancelUnfreezeV2Actuator(); + actuator.setChainBaseManager(null).setAny(getCancelUnfreezeV2Contract()); + assertThrows(ActuatorConstant.STORE_NOT_EXIST, + ContractValidateException.class, actuator::validate); + } + + @Test + public void testSupportAllowCancelUnfreezeV2() { + dbManager.getDynamicPropertiesStore().saveAllowCancelUnfreezeV2(0); + CancelUnfreezeV2Actuator actuator = new CancelUnfreezeV2Actuator(); + actuator.setChainBaseManager(dbManager.getChainBaseManager()) + .setAny(getCancelUnfreezeV2Contract()); + assertThrows( + "Not support CancelUnfreezeV2 transaction, need to be opened by the committee", + ContractValidateException.class, actuator::validate); + } + + @Test + public void testWrongIndex() { + dbManager.getDynamicPropertiesStore().saveAllowCancelUnfreezeV2(1); + long now = System.currentTimeMillis(); + AccountCapsule accountCapsule = dbManager.getAccountStore() + .get(ByteArray.fromHexString(OWNER_ADDRESS)); + accountCapsule.addUnfrozenV2List(BANDWIDTH, 1000000L, now + 14 * 24 * 3600 * 1000); + accountCapsule.addUnfrozenV2List(ENERGY, 2000000L, -1); + accountCapsule.addUnfrozenV2List(BANDWIDTH, 3000000L, now + 14 * 24 * 3600 * 1000); + accountCapsule.addUnfrozenV2List(ENERGY, 4000000L, now + 14 * 24 * 3600 * 1000); + accountCapsule.addUnfrozenV2List(BANDWIDTH, 4000000L, 100); + accountCapsule.addUnfrozenV2List(ENERGY, 4000000L, 100); + dbManager.getAccountStore().put(accountCapsule.createDbKey(), accountCapsule); + + CancelUnfreezeV2Actuator actuator = new CancelUnfreezeV2Actuator(); + actuator.setChainBaseManager(dbManager.getChainBaseManager()) + .setAny(getWrongIndexCancelUnfreezeV2Contract()); + assertThrows("The input index[-1] cannot be less than 0 and cannot be greater than " + + "the maximum index[5] of unfreezeV2!", + ContractValidateException.class, actuator::validate); + } + + @Test + public void testWrongIndexSize() { + dbManager.getDynamicPropertiesStore().saveAllowCancelUnfreezeV2(1); + long now = System.currentTimeMillis(); + AccountCapsule accountCapsule = dbManager.getAccountStore() + .get(ByteArray.fromHexString(OWNER_ADDRESS)); + accountCapsule.addUnfrozenV2List(BANDWIDTH, 1000000L, now + 14 * 24 * 3600 * 1000); + accountCapsule.addUnfrozenV2List(ENERGY, 2000000L, -1); + accountCapsule.addUnfrozenV2List(BANDWIDTH, 3000000L, now + 14 * 24 * 3600 * 1000); + accountCapsule.addUnfrozenV2List(ENERGY, 4000000L, now + 14 * 24 * 3600 * 1000); + accountCapsule.addUnfrozenV2List(BANDWIDTH, 4000000L, 100); + accountCapsule.addUnfrozenV2List(ENERGY, 4000000L, 100); + dbManager.getAccountStore().put(accountCapsule.createDbKey(), accountCapsule); + + CancelUnfreezeV2Actuator actuator = new CancelUnfreezeV2Actuator(); + actuator.setChainBaseManager(dbManager.getChainBaseManager()) + .setAny(getWrongIndexSizeCancelUnfreezeV2Contract()); + assertThrows("The size of the index cannot exceed the size of unfrozenV2!", + ContractValidateException.class, actuator::validate); + } + + @Test + public void testErrorContract() { + dbManager.getDynamicPropertiesStore().saveAllowCancelUnfreezeV2(1); + CancelUnfreezeV2Actuator actuator = new CancelUnfreezeV2Actuator(); + actuator.setChainBaseManager(dbManager.getChainBaseManager()).setAny(getErrorContract()); + assertThrows( + "contract type error, expected type [CancelUnfreezeV2Contract], " + + "real type[WithdrawExpireUnfreezeContract]", + ContractValidateException.class, actuator::validate); + } + + private Any getCancelUnfreezeV2Contract() { + return Any.pack(BalanceContract.CancelUnfreezeV2Contract.newBuilder() + .addAllIndex(Lists.newArrayList(0, 1, 2, 3)) + .setOwnerAddress(ByteString.copyFrom(ByteArray.fromHexString(OWNER_ADDRESS))).build() + ); + } + + private Any getCancelAllUnfreezeV2Contract() { + return Any.pack(BalanceContract.CancelUnfreezeV2Contract.newBuilder() + .setOwnerAddress(ByteString.copyFrom(ByteArray.fromHexString(OWNER_ADDRESS))).build() + ); + } + + private Any getWrongIndexSizeCancelUnfreezeV2Contract() { + return Any.pack(BalanceContract.CancelUnfreezeV2Contract.newBuilder() + .addAllIndex(Lists.newArrayList(0, 1, 2, 1, 3, 2, 4)) + .setOwnerAddress(ByteString.copyFrom(ByteArray.fromHexString(OWNER_ADDRESS))).build() + ); + } + + private Any getWrongIndexCancelUnfreezeV2Contract() { + return Any.pack(BalanceContract.CancelUnfreezeV2Contract.newBuilder() + .addAllIndex(Lists.newArrayList(-1, 1, 2, 1, 3, 2)) + .setOwnerAddress(ByteString.copyFrom(ByteArray.fromHexString(OWNER_ADDRESS))).build() + ); + } + + private Any getErrorContract() { + return Any.pack(BalanceContract.WithdrawExpireUnfreezeContract.newBuilder().setOwnerAddress( + ByteString.copyFrom(ByteArray.fromHexString(OWNER_ADDRESS))).build() + ); + } + + private Any getCancelUnfreezeV2ContractInvalidAddress() { + return Any.pack(BalanceContract.CancelUnfreezeV2Contract.newBuilder().setOwnerAddress( + ByteString.copyFrom(ByteArray.fromHexString(OWNER_ADDRESS_INVALID))).build()); + } + + private Any getCancelUnfreezeV2ContractInvalidAccount() { + return Any.pack(BalanceContract.CancelUnfreezeV2Contract.newBuilder().setOwnerAddress( + ByteString.copyFrom(ByteArray.fromHexString(OWNER_ACCOUNT_INVALID))).build() + ); + } +} \ No newline at end of file diff --git a/framework/src/test/java/org/tron/core/services/http/HttpServletTest.java b/framework/src/test/java/org/tron/core/services/http/HttpServletTest.java index 9775db0a954..823a8f7ea1b 100644 --- a/framework/src/test/java/org/tron/core/services/http/HttpServletTest.java +++ b/framework/src/test/java/org/tron/core/services/http/HttpServletTest.java @@ -139,6 +139,7 @@ public class HttpServletTest { private VoteWitnessAccountServlet voteWitnessAccountServlet; private WithdrawBalanceServlet withdrawBalanceServlet; private WithdrawExpireUnfreezeServlet withdrawExpireUnfreezeServlet; + private CancelUnfreezeV2Servlet cancelUnfreezeV2Servlet; private MockHttpServletRequest request; private MockHttpServletResponse response; @@ -275,6 +276,7 @@ public void setUp() { voteWitnessAccountServlet = new VoteWitnessAccountServlet(); withdrawBalanceServlet = new WithdrawBalanceServlet(); withdrawExpireUnfreezeServlet = new WithdrawExpireUnfreezeServlet(); + cancelUnfreezeV2Servlet = new CancelUnfreezeV2Servlet(); request = new MockHttpServletRequest(); request.setCharacterEncoding("UTF-8"); response = new MockHttpServletResponse(); @@ -526,6 +528,7 @@ public void doPostTest() { voteWitnessAccountServlet.doPost(request, response); withdrawBalanceServlet.doPost(request, response); withdrawExpireUnfreezeServlet.doPost(request, response); + cancelUnfreezeV2Servlet.doPost(request, response); } } diff --git a/plugins/build.gradle b/plugins/build.gradle index 22e37737731..457fb739058 100644 --- a/plugins/build.gradle +++ b/plugins/build.gradle @@ -26,7 +26,7 @@ configurations.getByName('checkstyleConfig') { dependencies { //local libraries compile fileTree(dir: 'libs', include: '*.jar') - testCompile group: 'junit', name: 'junit', version: '4.12' + testCompile group: 'junit', name: 'junit', version: '4.13.2' testCompile group: 'org.mockito', name: 'mockito-core', version: '2.13.0' testCompile group: 'org.hamcrest', name: 'hamcrest-junit', version: '1.0.0.1' testCompile group: 'org.testng', name: 'testng', version: '6.14.3' diff --git a/protocol/src/main/protos/api/api.proto b/protocol/src/main/protos/api/api.proto index 70297d0959b..1e37f425f50 100644 --- a/protocol/src/main/protos/api/api.proto +++ b/protocol/src/main/protos/api/api.proto @@ -281,6 +281,9 @@ service Wallet { rpc UnDelegateResource (UnDelegateResourceContract) returns (TransactionExtention) { } + rpc CancelUnfreezeV2 (CancelUnfreezeV2Contract) returns (TransactionExtention) { + } + //Please use UpdateAsset2 instead of this function. rpc UpdateAsset (UpdateAssetContract) returns (Transaction) { option (google.api.http) = { diff --git a/protocol/src/main/protos/core/Tron.proto b/protocol/src/main/protos/core/Tron.proto index e5145484f05..2ce19ba2708 100644 --- a/protocol/src/main/protos/core/Tron.proto +++ b/protocol/src/main/protos/core/Tron.proto @@ -374,6 +374,7 @@ message Transaction { WithdrawExpireUnfreezeContract = 56; DelegateResourceContract = 57; UnDelegateResourceContract = 58; + CancelUnfreezeV2Contract = 59; } ContractType type = 1; google.protobuf.Any parameter = 2; @@ -422,6 +423,7 @@ message Transaction { bytes orderId = 25; repeated MarketOrderDetail orderDetails = 26; int64 withdraw_expire_amount = 27; + int64 cancel_unfreezeV2_amount = 28; } message raw { @@ -482,6 +484,7 @@ message TransactionInfo { int64 packingFee = 27; int64 withdraw_expire_amount = 28; + int64 cancel_unfreezeV2_amount = 29; } message TransactionRet { diff --git a/protocol/src/main/protos/core/contract/balance_contract.proto b/protocol/src/main/protos/core/contract/balance_contract.proto index 90a2dfaa48d..14b531eaa6d 100644 --- a/protocol/src/main/protos/core/contract/balance_contract.proto +++ b/protocol/src/main/protos/core/contract/balance_contract.proto @@ -110,4 +110,9 @@ message UnDelegateResourceContract { ResourceCode resource = 2; int64 balance = 3; bytes receiver_address = 4; +} + +message CancelUnfreezeV2Contract { + bytes owner_address = 1; + repeated int32 index = 2; } \ No newline at end of file