From 23e27d9f05957411b8018e71f6856d98506709d0 Mon Sep 17 00:00:00 2001 From: zhang0125 Date: Tue, 1 Nov 2022 20:24:46 +0800 Subject: [PATCH 1/2] feat(memo-fee): add a proposal for memo fee 1. additional fee is charged when the transaction has memo information. --- .../org/tron/core/utils/ProposalUtil.java | 14 ++++++- .../org/tron/core/capsule/ReceiptCapsule.java | 3 ++ .../core/capsule/utils/TransactionUtil.java | 6 +-- .../core/store/DynamicPropertiesStore.java | 19 ++++++++++ .../common/parameter/CommonParameter.java | 4 ++ .../src/main/java/org/tron/core/Constant.java | 1 + .../src/main/java/org/tron/core/Wallet.java | 5 +++ .../java/org/tron/core/config/args/Args.java | 10 +++++ .../tron/core/consensus/ProposalService.java | 4 ++ .../main/java/org/tron/core/db/Manager.java | 37 +++++++++++++++++++ 10 files changed, 99 insertions(+), 4 deletions(-) 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 0fc7adba674..c18130d4221 100644 --- a/actuator/src/main/java/org/tron/core/utils/ProposalUtil.java +++ b/actuator/src/main/java/org/tron/core/utils/ProposalUtil.java @@ -575,6 +575,17 @@ public static void validator(DynamicPropertiesStore dynamicPropertiesStore, } break; } + case MEMO_FEE: { + if (!forkController.pass(ForkBlockVersionEnum.VERSION_4_6)) { + throw new ContractValidateException( + "Bad chain parameter id [MEMO_FEE]"); + } + if (value < 1 || value > 1_000_000_000) { + throw new ContractValidateException( + "This value[MEMO_FEE] is only allowed to be in the range 1-1000_000_000"); + } + break; + } default: break; } @@ -639,7 +650,8 @@ public enum ProposalType { // current value, value range ALLOW_TVM_LONDON(63), // 0, 1 ALLOW_HIGHER_LIMIT_FOR_MAX_CPU_TIME_OF_ONE_TX(65), // 0, 1 ALLOW_ASSET_OPTIMIZATION(66), // 0, 1 - ALLOW_NEW_REWARD_ALGO(67); // 0, 1 + ALLOW_NEW_REWARD_ALGO(67), // 0, 1 + MEMO_FEE(68); // 0, [1, 1000_000_000] private long code; diff --git a/chainbase/src/main/java/org/tron/core/capsule/ReceiptCapsule.java b/chainbase/src/main/java/org/tron/core/capsule/ReceiptCapsule.java index 3124494c67e..30fd1f7ecd3 100644 --- a/chainbase/src/main/java/org/tron/core/capsule/ReceiptCapsule.java +++ b/chainbase/src/main/java/org/tron/core/capsule/ReceiptCapsule.java @@ -24,6 +24,9 @@ public class ReceiptCapsule { @Setter private long multiSignFee; + @Getter + @Setter + private long memoFee; /** * Available energy of contract deployer before executing transaction */ 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 a2c3facd565..fc164bb9e36 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 @@ -72,9 +72,9 @@ public static TransactionInfoCapsule buildTransactionInfoInstance(TransactionCap } builder.setId(ByteString.copyFrom(trxCap.getTransactionId().getBytes())); ProgramResult programResult = trace.getRuntimeResult(); - long fee = - programResult.getRet().getFee() + traceReceipt.getEnergyFee() - + traceReceipt.getNetFee() + traceReceipt.getMultiSignFee(); + long fee = programResult.getRet().getFee() + traceReceipt.getEnergyFee() + + traceReceipt.getNetFee() + traceReceipt.getMultiSignFee() + + traceReceipt.getMemoFee(); boolean supportTransactionFeePool = trace.getTransactionContext().getStoreFactory() .getChainBaseManager().getDynamicPropertiesStore().supportTransactionFeePool(); 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 df37059f2ad..aa800266be5 100644 --- a/chainbase/src/main/java/org/tron/core/store/DynamicPropertiesStore.java +++ b/chainbase/src/main/java/org/tron/core/store/DynamicPropertiesStore.java @@ -187,6 +187,8 @@ public class DynamicPropertiesStore extends TronStoreWithRevoking private static final byte[] ALLOW_HIGHER_LIMIT_FOR_MAX_CPU_TIME_OF_ONE_TX = "ALLOW_HIGHER_LIMIT_FOR_MAX_CPU_TIME_OF_ONE_TX".getBytes(); + private static final byte[] MEMO_FEE = "MEMO_FEE".getBytes(); + @Autowired private DynamicPropertiesStore(@Value("properties") String dbName) { super(dbName); @@ -857,6 +859,12 @@ private DynamicPropertiesStore(@Value("properties") String dbName) { new BytesCapsule(ByteArray.fromLong(Long.MAX_VALUE))); } } + + try { + this.getMemoFee(); + } catch (IllegalArgumentException e) { + this.saveMemoFee(CommonParameter.getInstance().getMemoFee()); + } } public String intArrayToString(int[] a) { @@ -2528,6 +2536,17 @@ public long getAllowHigherLimitForMaxCpuTimeOfOneTx() { () -> new IllegalArgumentException(msg)); } + public long getMemoFee() { + return Optional.ofNullable(getUnchecked(MEMO_FEE)) + .map(BytesCapsule::getData) + .map(ByteArray::toLong) + .orElseThrow(() -> new IllegalArgumentException("not found MEMO_FEE")); + } + + public void saveMemoFee(long value) { + this.put(MEMO_FEE, new BytesCapsule(ByteArray.fromLong(value))); + } + private static class DynamicResourceProperties { private static final byte[] ONE_DAY_NET_LIMIT = "ONE_DAY_NET_LIMIT".getBytes(); 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 26aa7d44834..0331628d53c 100644 --- a/common/src/main/java/org/tron/common/parameter/CommonParameter.java +++ b/common/src/main/java/org/tron/common/parameter/CommonParameter.java @@ -561,6 +561,10 @@ public class CommonParameter { @Setter public long allowNewRewardAlgorithm; + @Getter + @Setter + public long memoFee = 0L; + 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/common/src/main/java/org/tron/core/Constant.java b/common/src/main/java/org/tron/core/Constant.java index af96fbea859..aa287b1d885 100644 --- a/common/src/main/java/org/tron/core/Constant.java +++ b/common/src/main/java/org/tron/core/Constant.java @@ -307,6 +307,7 @@ public class Constant { public static final String ALLOW_ACCOUNT_ASSET_OPTIMIZATION = "committee.allowAccountAssetOptimization"; public static final String ALLOW_ASSET_OPTIMIZATION = "committee.allowAssetOptimization"; + public static final String MEMO_FEE = "committee.memoFee"; public static final String LOCAL_HOST = "127.0.0.1"; diff --git a/framework/src/main/java/org/tron/core/Wallet.java b/framework/src/main/java/org/tron/core/Wallet.java index 33bd411c28f..7db0fd83c33 100755 --- a/framework/src/main/java/org/tron/core/Wallet.java +++ b/framework/src/main/java/org/tron/core/Wallet.java @@ -1103,6 +1103,11 @@ public Protocol.ChainParameters getChainParameters() { .setValue(dbManager.getDynamicPropertiesStore().useNewRewardAlgorithm() ? 1 : 0) .build()); + builder.addChainParameter(Protocol.ChainParameters.ChainParameter.newBuilder() + .setKey("getMemoFee") + .setValue(dbManager.getDynamicPropertiesStore().getMemoFee()) + .build()); + return builder.build(); } diff --git a/framework/src/main/java/org/tron/core/config/args/Args.java b/framework/src/main/java/org/tron/core/config/args/Args.java index b75664ed3c1..26a3250a369 100644 --- a/framework/src/main/java/org/tron/core/config/args/Args.java +++ b/framework/src/main/java/org/tron/core/config/args/Args.java @@ -1034,6 +1034,16 @@ public static void setParam(final String[] args, final String confFileName) { PARAMETER.blockCacheTimeout = config.getLong(Constant.BLOCK_CACHE_TIMEOUT); } + if (config.hasPath(Constant.MEMO_FEE)) { + PARAMETER.memoFee = config.getLong(Constant.MEMO_FEE); + if (PARAMETER.memoFee > 1_000_000_000) { + PARAMETER.memoFee = 1_000_000_000; + } + if (PARAMETER.memoFee < 0) { + PARAMETER.memoFee = 0; + } + } + logConfig(); } 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 92472335d20..50aee112819 100644 --- a/framework/src/main/java/org/tron/core/consensus/ProposalService.java +++ b/framework/src/main/java/org/tron/core/consensus/ProposalService.java @@ -288,6 +288,10 @@ public static boolean process(Manager manager, ProposalCapsule proposalCapsule) manager.getDynamicPropertiesStore().saveNewRewardAlgorithmEffectiveCycle(); break; } + case MEMO_FEE: { + manager.getDynamicPropertiesStore().saveMemoFee(entry.getValue()); + break; + } default: find = false; break; diff --git a/framework/src/main/java/org/tron/core/db/Manager.java b/framework/src/main/java/org/tron/core/db/Manager.java index 80be86403ba..fa75deafb3f 100644 --- a/framework/src/main/java/org/tron/core/db/Manager.java +++ b/framework/src/main/java/org/tron/core/db/Manager.java @@ -855,6 +855,42 @@ public void consumeMultiSignFee(TransactionCapsule trx, TransactionTrace trace) } } + public void consumeMemoFee(TransactionCapsule trx, TransactionTrace trace) + throws AccountResourceInsufficientException { + if (trx.getInstance().getRawData().getData().isEmpty()) { + // no memo + return; + } + + long fee = getDynamicPropertiesStore().getMemoFee(); + if (fee == 0) { + return; + } + + List contracts = trx.getInstance().getRawData().getContractList(); + for (Contract contract : contracts) { + byte[] address = TransactionCapsule.getOwner(contract); + AccountCapsule accountCapsule = getAccountStore().get(address); + try { + if (accountCapsule != null) { + adjustBalance(getAccountStore(), accountCapsule, -fee); + + if (getDynamicPropertiesStore().supportBlackHoleOptimization()) { + getDynamicPropertiesStore().burnTrx(fee); + } else { + adjustBalance(getAccountStore(), this.getAccountStore().getBlackhole(), +fee); + } + } + } catch (BalanceInsufficientException e) { + throw new AccountResourceInsufficientException( + String.format("account %s insufficient balance[%d] to memo fee", + StringUtil.encode58Check(address), fee)); + } + } + + trace.getReceipt().setMemoFee(fee); + } + public void consumeBandwidth(TransactionCapsule trx, TransactionTrace trace) throws ContractValidateException, AccountResourceInsufficientException, TooBigTransactionResultException { @@ -1338,6 +1374,7 @@ public TransactionInfo processTransaction(final TransactionCapsule trxCap, Block consumeBandwidth(trxCap, trace); consumeMultiSignFee(trxCap, trace); + consumeMemoFee(trxCap, trace); trace.init(blockCap, eventPluginLoaded); trace.checkIsConstant(); From 8a3877b1e3bcb2cfb2d1cea5908b2596c399719b Mon Sep 17 00:00:00 2001 From: zhang0125 Date: Wed, 2 Nov 2022 17:10:47 +0800 Subject: [PATCH 2/2] feat(memo-fee): add a interface of memo fee --- .../org/tron/core/utils/ProposalUtil.java | 4 +- .../core/store/DynamicPropertiesStore.java | 16 +++++++- .../src/main/java/org/tron/core/Wallet.java | 8 ++++ .../tron/core/consensus/ProposalService.java | 4 ++ .../services/http/FullNodeHttpApiService.java | 3 ++ .../http/GetMemoFeePricesServlet.java | 37 +++++++++++++++++++ 6 files changed, 69 insertions(+), 3 deletions(-) create mode 100644 framework/src/main/java/org/tron/core/services/http/GetMemoFeePricesServlet.java 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 c18130d4221..8235daa9cdb 100644 --- a/actuator/src/main/java/org/tron/core/utils/ProposalUtil.java +++ b/actuator/src/main/java/org/tron/core/utils/ProposalUtil.java @@ -580,9 +580,9 @@ public static void validator(DynamicPropertiesStore dynamicPropertiesStore, throw new ContractValidateException( "Bad chain parameter id [MEMO_FEE]"); } - if (value < 1 || value > 1_000_000_000) { + if (value < 0 || value > 1_000_000_000) { throw new ContractValidateException( - "This value[MEMO_FEE] is only allowed to be in the range 1-1000_000_000"); + "This value[MEMO_FEE] is only allowed to be in the range 0-1000_000_000"); } break; } 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 aa800266be5..9f4c9444cbe 100644 --- a/chainbase/src/main/java/org/tron/core/store/DynamicPropertiesStore.java +++ b/chainbase/src/main/java/org/tron/core/store/DynamicPropertiesStore.java @@ -188,6 +188,7 @@ public class DynamicPropertiesStore extends TronStoreWithRevoking "ALLOW_HIGHER_LIMIT_FOR_MAX_CPU_TIME_OF_ONE_TX".getBytes(); private static final byte[] MEMO_FEE = "MEMO_FEE".getBytes(); + private static final byte[] MEMO_FEE_HISTORY = "MEMO_FEE_HISTORY".getBytes(); @Autowired private DynamicPropertiesStore(@Value("properties") String dbName) { @@ -863,7 +864,9 @@ private DynamicPropertiesStore(@Value("properties") String dbName) { try { this.getMemoFee(); } catch (IllegalArgumentException e) { - this.saveMemoFee(CommonParameter.getInstance().getMemoFee()); + long memoFee = CommonParameter.getInstance().getMemoFee(); + this.saveMemoFee(memoFee); + this.saveMemoFeeHistory("0:" + memoFee); } } @@ -2547,6 +2550,17 @@ public void saveMemoFee(long value) { this.put(MEMO_FEE, new BytesCapsule(ByteArray.fromLong(value))); } + public String getMemoFeeHistory() { + return Optional.ofNullable(getUnchecked(MEMO_FEE_HISTORY)) + .map(BytesCapsule::getData) + .map(ByteArray::toStr) + .orElseThrow(() -> new IllegalArgumentException("not found MEMO_FEE_HISTORY")); + } + + public void saveMemoFeeHistory(String value) { + this.put(MEMO_FEE_HISTORY, new BytesCapsule(ByteArray.fromString(value))); + } + private static class DynamicResourceProperties { private static final byte[] ONE_DAY_NET_LIMIT = "ONE_DAY_NET_LIMIT".getBytes(); diff --git a/framework/src/main/java/org/tron/core/Wallet.java b/framework/src/main/java/org/tron/core/Wallet.java index 7db0fd83c33..5f77a651ea0 100755 --- a/framework/src/main/java/org/tron/core/Wallet.java +++ b/framework/src/main/java/org/tron/core/Wallet.java @@ -4085,5 +4085,13 @@ public Block getBlock(GrpcAPI.BlockReq request) { return block.toBuilder().clearTransactions().build(); } + public String getMemoFeePrices() { + try { + return chainBaseManager.getDynamicPropertiesStore().getMemoFeeHistory(); + } catch (Exception e) { + logger.error("getMemoFeePrices failed, error is {}", e.getMessage()); + } + return null; + } } 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 50aee112819..0d2ed945f42 100644 --- a/framework/src/main/java/org/tron/core/consensus/ProposalService.java +++ b/framework/src/main/java/org/tron/core/consensus/ProposalService.java @@ -290,6 +290,10 @@ public static boolean process(Manager manager, ProposalCapsule proposalCapsule) } case MEMO_FEE: { manager.getDynamicPropertiesStore().saveMemoFee(entry.getValue()); + // update memo fee history + manager.getDynamicPropertiesStore().saveMemoFeeHistory( + manager.getDynamicPropertiesStore().getMemoFeeHistory() + + "," + proposalCapsule.getExpirationTime() + ":" + entry.getValue()); break; } default: 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 031bd38e50c..30c0b380551 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 @@ -293,6 +293,8 @@ public class FullNodeHttpApiService implements Service { private GetBandwidthPricesServlet getBandwidthPricesServlet; @Autowired private GetBlockServlet getBlockServlet; + @Autowired + private GetMemoFeePricesServlet getMemoFeePricesServlet; private static String getParamsFile(String fileName) { InputStream in = Thread.currentThread().getContextClassLoader() @@ -542,6 +544,7 @@ public void start() { context.addServlet(new ServletHolder(getBandwidthPricesServlet), "/wallet/getbandwidthprices"); context.addServlet(new ServletHolder(getBlockServlet), "/wallet/getblock"); + context.addServlet(new ServletHolder(getMemoFeePricesServlet), "/wallet/getmemofee"); int maxHttpConnectNumber = Args.getInstance().getMaxHttpConnectNumber(); if (maxHttpConnectNumber > 0) { diff --git a/framework/src/main/java/org/tron/core/services/http/GetMemoFeePricesServlet.java b/framework/src/main/java/org/tron/core/services/http/GetMemoFeePricesServlet.java new file mode 100644 index 00000000000..8d5f46d8236 --- /dev/null +++ b/framework/src/main/java/org/tron/core/services/http/GetMemoFeePricesServlet.java @@ -0,0 +1,37 @@ +package org.tron.core.services.http; + +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; + + +@Component +@Slf4j(topic = "API") +public class GetMemoFeePricesServlet extends RateLimiterServlet { + + @Autowired + private Wallet wallet; + + protected void doGet(HttpServletRequest request, HttpServletResponse response) { + try { + String reply = wallet.getMemoFeePrices(); + if (reply != null) { + JSONObject jsonObject = new JSONObject(); + jsonObject.put("prices", reply); + response.getWriter().println(jsonObject); + } else { + response.getWriter().println("{}"); + } + } catch (Exception e) { + Util.processError(e, response); + } + } + + protected void doPost(HttpServletRequest request, HttpServletResponse response) { + doGet(request, response); + } +}