tip: 370
title: Node support conditionalized stop
author: halibobo1205@gmail.com
discussions to: https://github.com/tronprotocol/TIPs/issues/370
status: Final
type: Standards Track
category: Core
created: 2022-03-05
This TIP describes feature about java-tron stop block synchronization according to specified conditions, such as a given block height,block timestamp, make the blockchain stop exactly at a specific state.
There are some scenarios that require java-tron to be stopped according to given conditions, such as given block time, block height, used for database snapshots, statistics, etc. So we decided to support this feature,three conditions are considered: block time,block height,block sync count.
Currently java-tron does not have a very convenient way to get a specific database state, previously perhaps by manual, or additional scripting, which results in either inaccuracy or high maintenance costs. Therefore a convenient way to achieve this purpose is needed.
For this feature,three conditions are supported,block time,block height, block sync count after node starts , if there has more than one condition , triggered in order of activation. persistent database mentioned below is leveldb or rocksdb.
- block time, cron expression is support.
Stop and exit when
blockHeaderinpersistent databaseis matched with a given block time. - block height, the value must be equal or greater than the blockHeader at startup, if not will be ignored.
Stop and exit when
blockHeaderinpersistent databaseis matched with a given block height. - block sync coun, the value must be greater than 0, if not will be ignored.
Stop and exit when
block sync countarrive at a given block count since node start.
Add three new settings to the config.conf file as follows.
net {
type = mainnet
# type = testnet
}
... ...
crypto {
engine = "eckey"
}
node {
######### conditionalized stop start
shutdown {
# block time
BlockTime = "54 59 08 * * ?"
# block height
BlockHeight = 33350800
# given block sync number after start
BlockCount = 12
}
######### conditionalized stop start
# trust node for solidity node
# trustNode = "ip:port"
trustNode = "127.0.0.1:50051"
... ...
add new method getFromRoot, Input & Output are same as exist get method
byte[] getFromRoot(byte[] key) throws ItemNotFoundException;Because of confirmed block principle,TronStoreWithRevoking is composed of snapshotImpl (in-memory database ) and snapshotRoot(persistent DB). The get and put operations give priority to in-memory snapshotImpl queries, so the current get operation cannot directly know the snapshotRoot state, such as LatestBlockHeaderNumber,and LatestSolidifiedBlockNum is not updated in real-time. So it was decided to add a new method to query snapshotRoot directly.

init stop sync block height in Manager.class, update stop sync block height when pushBlock if node.shutdown.BlockTime if has a priority Trigger,see the implementation for details.
Check stop sync block height when processBlock in TronNetDelegate.class,when matched withLatestBlockHeaderNumber in persistent DB, stop sync and exit,,see the implementation for details.
Get LatestBlockHeaderNumber by adding a new method to query persistent DB directly, add a new exit thread, when LatestBlockHeaderNumber in persistent DB meets the given condition, the node stops synchronization and exits. Because it is judged to stop before the pushBlock, for blocks that have been persisted, the data correctness and integrity have been guaranteed and verified by two-thirds SRs.
- Add New Method For Query
persistent DB.
public class Chainbase implements IRevokingDB {
@Override
public byte[] getFromRoot(byte[] key) throws ItemNotFoundException {
byte[] value = head().getRoot().get(key);
if (value == null) {
throw new ItemNotFoundException();
}
return value;
}
}- Init Shutdown Condition According To A Given Condition.
public class Manager {
// ... ...
private BlockingQueue<FilterTriggerCapsule> filterCapsuleQueue;
@Getter
private volatile long latestSolidityNumShutDown; // `stop sync block height`
// ... ...
@PostConstruct
public void init() {
// .. ...
TransactionRegister.registerActuator();
//*********************** init `stop sync block height` start
long exitHeight = CommonParameter.getInstance().getShutdownBlockHeight();
long exitCount = CommonParameter.getInstance().getShutdownBlockCount();
if (exitCount > 0 && (exitHeight < 0 || exitHeight > headNum + exitCount)) {
CommonParameter.getInstance().setShutdownBlockHeight(headNum + exitCount);
}
if (CommonParameter.getInstance().getShutdownBlockHeight() < headNum) {
logger.info("ShutDownBlockHeight {} is less than headNum {},ignored.",
CommonParameter.getInstance().getShutdownBlockHeight(), headNum);
CommonParameter.getInstance().setShutdownBlockHeight(-1);
}
// init
latestSolidityNumShutDown = CommonParameter.getInstance().getShutdownBlockHeight();
//*********************** init `stop sync block height` end
}
public synchronized void pushBlock(final BlockCapsule block) {
// ... ...
logger.info("Block num: {}, re-push-size: {}, pending-size: {}, "
+ "block-tx-size: {}, verify-tx-size: {}",
block.getNum(), rePushTransactions.size(), pendingTransactions.size(),
block.getTransactions().size(), txs.size());
// ******* update `stop sync block height` when `pushBlock ` if `node.shutdown.BlockTime` if has a priority Trigger
if (CommonParameter.getInstance().getShutdownBlockTime() != null
&& CommonParameter.getInstance().getShutdownBlockTime()
.isSatisfiedBy(new Date(block.getTimeStamp()))) {
latestSolidityNumShutDown = block.getNum();
CommonParameter.getInstance().setShutdownBlockTime(null);
}
// ******* add end *******
// ... ...
}
}
- Check
stop sync block heightAnd Exit.
public class TronNetDelegate {
// ... ...
private long timeout = 1000;
private volatile boolean hitDown = false; // ******* hitDown flag
private Thread hitThread; // ****** hitThread standalone
@PostConstruct
public void init() { // ****** add new Thread for exit.
hitThread = new Thread(() -> {
LockSupport.park();
// to Guarantee Some other thread invokes unpark with the current thread as the target
if (hitDown) {
System.exit(0);
}
});
hitThread.setName("hit-thread");
hitThread.start();
}
public void processBlock(BlockCapsule block, boolean isSync) throws P2pException {
// *********. check and exit
if (!hitDown && dbManager.getLatestSolidityNumShutDown() > 0
&& dbManager.getLatestSolidityNumShutDown() == dbManager.getDynamicPropertiesStore()
.getLatestBlockHeaderNumberFromDB()) {
hitDown = true;
LockSupport.unpark(hitThread);
return;
}
if (hitDown) {
return;
}
BlockId blockId = block.getBlockId();
// ... ...
}
}
Copyright and related rights waived via CC0.