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
blockHeader
inpersistent database
is 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
blockHeader
inpersistent database
is 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 count
arrive 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 height
And 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();
// ... ...
}
}