Skip to content

Latest commit

 

History

History
186 lines (160 loc) · 7.74 KB

tip-370.md

File metadata and controls

186 lines (160 loc) · 7.74 KB
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

Simple Summary

This TIP describes feature about java-tron stop block synchronization according to specified conditions, such as a given block heightblock timestamp, make the blockchain stop exactly at a specific state.

Abstract

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.

Motivation

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.

Specification

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.

1. Conditions

  1. block time, cron expression is support. Stop and exit when blockHeader in persistent database is matched with a given block time.
  2. block height, the value must be equal or greater than the blockHeader at startup, if not will be ignored. Stop and exit when blockHeader in persistent database is matched with a given block height.
  3. 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.

2. How To Set Conditions.

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"
  ... ...

3. Add New Method To Query Persistent DB Directly For LatestBlockHeaderNumber.

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. image

4. Init Shutdown Condition According To A Given Condition.

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.

5. Check stop sync block height And Exit.

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.

Rationale

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.

Implementation

  1. 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;
  }
}
  1. 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 *******
  // ... ...
 }
}
        
  1. 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();
// ... ...
}

}