Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
38 changes: 30 additions & 8 deletions chainbase/src/main/java/org/tron/core/ChainBaseManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.tron.common.parameter.CommonParameter;
import org.tron.common.storage.metric.DbStatService;
import org.tron.common.utils.ForkController;
import org.tron.common.utils.Sha256Hash;
Expand Down Expand Up @@ -244,10 +246,38 @@ public class ChainBaseManager {
@Setter
private long lowestBlockNum = -1; // except num = 0.

// first block of transactionRetStore; head + 1 when the store is empty,
// Long.MAX_VALUE when receipts are not persisted
@Getter
private long lowestBlockNumOfReceiptStore = -1;
Comment thread
SeriousCoding789 marked this conversation as resolved.

@Getter
@Setter
private long latestSaveBlockTime;

@PostConstruct
private void init() {
this.lowestBlockNum = this.blockIndexStore.getLimitNumber(1, 1).stream()
.map(BlockId::getNum).findFirst().orElse(0L);
this.nodeType = getLowestBlockNum() > 1 ? NodeType.LITE : NodeType.FULL;
this.latestSaveBlockTime = System.currentTimeMillis();
}

/**
* Reads the lowest receipt block from the store itself, not from snapshot metadata; an empty
* store means receipts begin with the next executed block. With receipt persistence off
* the store never grows, so no lower bound exists. Must run after checkpoint recovery (so the
* last session's tail is visible) and before any session is built ({@code getNext} does
* not merge in-flight layers).
*/
public void initLowestBlockNumOfReceiptStore() {
boolean persistReceipts = BooleanUtils.toBoolean(CommonParameter.getInstance()
.getStorage().getTransactionHistorySwitch());
this.lowestBlockNumOfReceiptStore = persistReceipts
? this.transactionRetStore.getLowestReceiptBlockNum().orElseGet(() -> getHeadBlockNum() + 1)
: Long.MAX_VALUE;
Comment thread
0xbigapple marked this conversation as resolved.
}

// for test only
public List<ByteString> getWitnesses() {
return witnessScheduleStore.getActiveWitnesses();
Expand Down Expand Up @@ -389,14 +419,6 @@ public long getNextBlockSlotTime() {
+ slotCount * BLOCK_PRODUCED_INTERVAL;
}

@PostConstruct
private void init() {
this.lowestBlockNum = this.blockIndexStore.getLimitNumber(1, 1).stream()
.map(BlockId::getNum).findFirst().orElse(0L);
this.nodeType = getLowestBlockNum() > 1 ? NodeType.LITE : NodeType.FULL;
this.latestSaveBlockTime = System.currentTimeMillis();
}

public void shutdown() {
dbStatService.shutdown();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,10 @@ public void setErrorCode(Result.code code) {
this.transactionResult = this.transactionResult.toBuilder().setRet(code).build();
}

public void setResultCode(contractResult code) {
this.transactionResult = this.transactionResult.toBuilder().setContractRet(code).build();
}

public long getShieldedTransactionFee() {
return transactionResult.getShieldedTransactionFee();
}
Expand Down Expand Up @@ -184,4 +188,4 @@ public byte[] getData() {
public Result getInstance() {
return this.transactionResult;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package org.tron.core.store;

import com.google.common.primitives.Longs;
import com.google.protobuf.ByteString;
import java.util.Objects;
import java.util.OptionalLong;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.BooleanUtils;
import org.springframework.beans.factory.annotation.Autowired;
Expand Down Expand Up @@ -37,6 +39,18 @@ public void put(byte[] key, TransactionRetCapsule item) {
}
}

/**
* Lowest block number that has receipts, or empty when the store has none. On a LiteNode
* this is generally above the lowest block: a snapshot ships block bodies but no receipts.
*
* <p>Startup use only — must run before any session is built. With in-flight snapshot
* layers, {@code getNext} does not merge deletions correctly.
*/
public OptionalLong getLowestReceiptBlockNum() {
return revokingDB.getNext(ByteArray.fromLong(0), 1).keySet().stream()
.mapToLong(Longs::fromByteArray).findFirst();
}

public TransactionInfoCapsule getTransactionInfo(byte[] key) throws BadItemException {
long blockNumber = transactionStore.getBlockNumber(key);
if (blockNumber == -1) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package org.tron.core.exception.jsonrpc;

public class JsonRpcExecutionRevertedException extends JsonRpcException {

public JsonRpcExecutionRevertedException(String message) {
super(message);
}

public JsonRpcExecutionRevertedException(String message, Object data) {
super(message, data);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package org.tron.core.exception.jsonrpc;

/**
* Thrown when a request targets data the node does not have: history pruned on a LiteNode,
* or receipts and logs on a node that does not persist them. Maps to JSON-RPC error code 4444
* "Pruned history unavailable", as standardized by the Ethereum Execution API (EIP-4444).
*/
public class JsonRpcPrunedHistoryException extends JsonRpcException {

public JsonRpcPrunedHistoryException(String message) {
super(message);
}

public JsonRpcPrunedHistoryException(String message, Object data) {
super(message, data);
}
}
5 changes: 4 additions & 1 deletion common/src/main/resources/reference.conf
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,10 @@ storage {

db.directory = "database" # Database directory under the node --output-directory path.

# Whether to write transaction result in transactionRetStore
# Whether to store transaction receipts (fee, energy usage, logs) in transactionRetStore.
# When "off", the JSON-RPC methods eth_getBlockReceipts, eth_getLogs and eth_newFilter
# return error 4444, including for blocks whose receipts were stored while the switch
# was "on". Queries for the genesis block alone are still answered.
transHistory.switch = "on"

# Per-database LevelDB option overrides.Default: empty. All databases use global LevelDB settings.
Expand Down
14 changes: 14 additions & 0 deletions framework/src/main/java/org/tron/core/Wallet.java
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,7 @@
import org.tron.protos.Protocol.Transaction.Contract;
import org.tron.protos.Protocol.Transaction.Contract.ContractType;
import org.tron.protos.Protocol.Transaction.Result.code;
import org.tron.protos.Protocol.Transaction.Result.contractResult;
import org.tron.protos.Protocol.TransactionInfo;
import org.tron.protos.contract.AssetIssueContractOuterClass.AssetIssueContract;
import org.tron.protos.contract.BalanceContract;
Expand Down Expand Up @@ -743,6 +744,18 @@ public long getHeadBlockNum() {
return chainBaseManager.getHeadBlockNum();
}

public boolean isLiteNode() {
return chainBaseManager.isLiteNode();
}

public long getLowestBlockNum() {
return chainBaseManager.getLowestBlockNum();
}

public long getLowestBlockNumOfReceiptStore() {
return chainBaseManager.getLowestBlockNumOfReceiptStore();
}

public BlockCapsule getBlockCapsuleByNum(long blockNum) {
try {
return chainBaseManager.getBlockByNum(blockNum);
Expand Down Expand Up @@ -3208,6 +3221,7 @@ public Transaction callConstantContract(TransactionCapsule trxCap,
}
if (result.isRevert()) {
ret.setStatus(0, code.FAILED);
ret.setResultCode(contractResult.REVERT);
retBuilder.setMessage(ByteString.copyFromUtf8("REVERT opcode executed"))
.build();
}
Expand Down
1 change: 1 addition & 0 deletions framework/src/main/java/org/tron/core/db/Manager.java
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,7 @@ public void init() {
trieService.setChainBaseManager(chainBaseManager);
revokingStore.disable();
revokingStore.check();
chainBaseManager.initLowestBlockNumOfReceiptStore();
Comment thread
0xbigapple marked this conversation as resolved.
transactionCache.initCache();
rewardViCalService.init();
this.setProposalController(ProposalController.createInstance(this));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import org.tron.common.utils.StringUtil;
import org.tron.core.Wallet;
import org.tron.core.exception.jsonrpc.JsonRpcInvalidParamsException;
import org.tron.core.exception.jsonrpc.JsonRpcPrunedHistoryException;
import org.tron.protos.Protocol.Block;
import org.tron.protos.Protocol.Transaction;
import org.tron.protos.Protocol.Transaction.Contract.ContractType;
Expand Down Expand Up @@ -61,6 +62,7 @@ public class JsonRpcApiUtil {
public static final String TAG_PENDING_SUPPORT_ERROR = "TAG pending not supported";
public static final String TAG_SAFE_SUPPORT_ERROR = "TAG safe not supported";
public static final String BLOCK_NUM_ERROR = "invalid block number";
public static final String PRUNED_HISTORY_ERROR = "Pruned history unavailable";
public static final String TX_INDEX_ERROR = "invalid index value";

private static final SecureRandom random = new SecureRandom();
Expand Down Expand Up @@ -633,7 +635,7 @@ public static long parseBlockTag(String tag, Wallet wallet)
return wallet.getHeadBlockNum();
}
if (EARLIEST_STR.equalsIgnoreCase(tag)) {
return 0;
return resolveEarliestBlockNum(wallet);
}
if (FINALIZED_STR.equalsIgnoreCase(tag)) {
return wallet.getSolidBlockNum();
Expand Down Expand Up @@ -697,6 +699,47 @@ public static long parseBlockNumber(String blockNumOrTag, Wallet wallet)
return parseBlockNumber(blockNumOrTag);
}

/**
* The lowest block for which everything the node persists is available; what "earliest"
* resolves to. On a LiteNode this is the
* lowest receipt block when receipts are persisted, otherwise the lowest block (receipt endpoints
* answer 4444 on such a node regardless of this value). On a FullNode it is genesis.
*/
public static long resolveEarliestBlockNum(Wallet wallet) {
if (!wallet.isLiteNode()) {
return 0;
}
long lowestReceiptBlockNum = wallet.getLowestBlockNumOfReceiptStore();
return lowestReceiptBlockNum == Long.MAX_VALUE
? wallet.getLowestBlockNum() : lowestReceiptBlockNum;
}

/**
* Rejects a query for a block below the LiteNode pruning cutoff with error code 4444.
* Raw primitive — no genesis exemption; callers own that semantics.
*/
public static void checkPrunedBlockHistory(long blockNum, Wallet wallet)
throws JsonRpcPrunedHistoryException {
if (wallet.isLiteNode() && blockNum < wallet.getLowestBlockNum()) {
Comment thread
SeriousCoding789 marked this conversation as resolved.
throw new JsonRpcPrunedHistoryException(PRUNED_HISTORY_ERROR);
}
}

/**
* Receipt form of {@link #checkPrunedBlockHistory(long, Wallet)} for endpoints that read
* receipts or logs; their lower bound is the lowest receipt block. Same raw-primitive
* contract. Receipt persistence is a per-node switch independent of node type, so a node
* that never persists receipts is rejected before the LiteNode gate.
*/
public static void checkPrunedReceiptHistory(long blockNum, Wallet wallet)
throws JsonRpcPrunedHistoryException {
long lowestReceiptBlockNum = wallet.getLowestBlockNumOfReceiptStore();
if (lowestReceiptBlockNum == Long.MAX_VALUE
|| (wallet.isLiteNode() && blockNum < lowestReceiptBlockNum)) {
throw new JsonRpcPrunedHistoryException(PRUNED_HISTORY_ERROR);
Comment thread
SeriousCoding789 marked this conversation as resolved.
}
}

/**
* Max hex digits of a 32-bit int (0x7FFFFFFF). A transaction index fits a signed int, so the
* longest valid input is "0x" + 8 hex digits; the +2 in the guard covers the prefix.
Expand Down
Loading
Loading