简述

witness 即在tron链中就是产块节点的代名词,一般称为SR
一般节点不产块,要配置成witness,需要启动时指定私钥,并使用--witness-w指定成为产块节点。

witness 加载过程

有两种加载方式:

  1. 参数或配置文件
  2. 指定localwitness启动

参数或配置文件

通过参数指定为witness节点,但是私钥建议写在配置文件中,否则ps查看一下进程就能看到启动参数,就全暴露了,但是如果有人能上机器,也能查看配置文件。

1
java -jar FullNode.jar --witness -p xxxxxxxxxxxxxxxxxx

输入上面的命令后,节点就会以SR类型启动,具体是如何加载的,调用栈如下:

1
2
3
4
5
6
7
FullNode.main()
\--Args.setParam(args, Constant.TESTNET_CONF); //381
\--PARAMETER.privateKey //优先加载 参数
\--Constant.LOCAL_WITNESS //或加载 配置文件
\--LocalWitnesses.setPrivateKeys //或从 keystore 中加载
FullNode.startup()
\--ConsensusService.start() // 加载localwitness

Args.setParam 加载过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
@Slf4j(topic = "app")
@NoArgsConstructor
@Component
public class Args extends CommonParameter {

@Getter
@Setter
private static LocalWitnesses localWitnesses = new LocalWitnesses();


public static void setParam(final String[] args, final String confFileName) {
// 省略部分代码
...
// PARAMETER.privateKey 即 -p 或 --private-key 参数
if (StringUtils.isNoneBlank(PARAMETER.privateKey)) {
localWitnesses = (new LocalWitnesses(PARAMETER.privateKey));
if (StringUtils.isNoneBlank(PARAMETER.witnessAddress)) {
byte[] bytes = Commons.decodeFromBase58Check(PARAMETER.witnessAddress);
if (bytes != null) {
localWitnesses.setWitnessAccountAddress(bytes);
logger.debug("Got localWitnessAccountAddress from cmd");
} else {
PARAMETER.witnessAddress = "";
logger.warn(IGNORE_WRONG_WITNESS_ADDRESS_FORMAT);
}
}
// 初始化 witness
localWitnesses.initWitnessAccountAddress(PARAMETER.isECKeyCryptoEngine());
logger.debug("Got privateKey from cmd");
} else if (config.hasPath(Constant.LOCAL_WITNESS)) {
// Constant.LOCAL_WITNESS 即,配置文件中的 localwitness 这个配置项,可以配置多个
// 作用就是一个 java-tron 服务,配置多个witness产块,这样做的话,要约成本,一台机器就可以配置多个
localWitnesses = new LocalWitnesses();
List<String> localwitness = config.getStringList(Constant.LOCAL_WITNESS);
localWitnesses.setPrivateKeys(localwitness);
witnessAddressCheck(config);
localWitnesses.initWitnessAccountAddress(PARAMETER.isECKeyCryptoEngine());
logger.debug("Got privateKey from config.conf");
} else if (config.hasPath(Constant.LOCAL_WITNESS_KEYSTORE)) {
// 通过 keystore 加载
localWitnesses = new LocalWitnesses();
List<String> privateKeys = new ArrayList<String>();
if (PARAMETER.isWitness()) {
List<String> localwitness = config.getStringList(Constant.LOCAL_WITNESS_KEYSTORE);
if (localwitness.size() > 0) {
String fileName = System.getProperty("user.dir") + "/" + localwitness.get(0);
String password;
if (StringUtils.isEmpty(PARAMETER.password)) {
System.out.println("Please input your password.");
password = WalletUtils.inputPassword();
} else {
password = PARAMETER.password;
PARAMETER.password = null;
}

try {
Credentials credentials = WalletUtils
.loadCredentials(password, new File(fileName));
SignInterface sign = credentials.getSignInterface();
String prikey = ByteArray.toHexString(sign.getPrivateKey());
privateKeys.add(prikey);
} catch (IOException | CipherException e) {
logger.error(e.getMessage());
logger.error("Witness node start failed!");
exit(-1);
}
}
}
localWitnesses.setPrivateKeys(privateKeys);
witnessAddressCheck(config);
localWitnesses.initWitnessAccountAddress(PARAMETER.isECKeyCryptoEngine());
logger.debug("Got privateKey from keystore");
}
// 省略部分代码
...
}
}

共识阶段加载

ConsensusService 主要控制共识相关,在启动时。

1
2
3
ConsensusService.start
\--Consensus.start
\--DposService.start // 对共抽的抽象,设计上方便切换共识

几个关注的点:

  1. 设置成witness节点
  2. 加载前面的私钥
  3. 启动共识
  4. 更新updateWitness
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
public void start() {
Param param = Param.getInstance();
// 设置成 witness 节点
param.setEnable(parameter.isWitness());
param.setGenesisBlock(parameter.getGenesisBlock());
param.setMinParticipationRate(parameter.getMinParticipationRate());
param.setBlockProduceTimeoutPercent(Args.getInstance().getBlockProducedTimeOut());
param.setNeedSyncCheck(parameter.isNeedSyncCheck());
param.setAgreeNodeCount(parameter.getAgreeNodeCount());
List<Miner> miners = new ArrayList<>();
// 拿到私钥列表
List<String> privateKeys = Args.getLocalWitnesses().getPrivateKeys();
if (privateKeys.size() > 1) {
// 前面说了,可以配置多个 私钥,所以这里遍历
for (String key : privateKeys) {
byte[] privateKey = fromHexString(key);
byte[] privateKeyAddress = SignUtils
.fromPrivate(privateKey, Args.getInstance().isECKeyCryptoEngine()).getAddress();
WitnessCapsule witnessCapsule = witnessStore.get(privateKeyAddress);
if (null == witnessCapsule) {
logger.warn("Witness {} is not in witnessStore.", Hex.toHexString(privateKeyAddress));
}
// 封装到 Miner 中,在产块时私钥相关校验
Miner miner = param.new Miner(privateKey, ByteString.copyFrom(privateKeyAddress),
ByteString.copyFrom(privateKeyAddress));
miners.add(miner);
logger.info("Add witness: {}, size: {}",
Hex.toHexString(privateKeyAddress), miners.size());
}
} else {
byte[] privateKey =
fromHexString(Args.getLocalWitnesses().getPrivateKey());
byte[] privateKeyAddress = SignUtils.fromPrivate(privateKey,
Args.getInstance().isECKeyCryptoEngine()).getAddress();
byte[] witnessAddress = Args.getLocalWitnesses().getWitnessAccountAddress(
Args.getInstance().isECKeyCryptoEngine());
WitnessCapsule witnessCapsule = witnessStore.get(witnessAddress);
if (null == witnessCapsule) {
logger.warn("Witness {} is not in witnessStore.", Hex.toHexString(witnessAddress));
}
Miner miner = param.new Miner(privateKey, ByteString.copyFrom(privateKeyAddress),
ByteString.copyFrom(witnessAddress));
miners.add(miner);
}

param.setMiners(miners);
param.setBlockHandle(blockHandle);
param.setPbftInterface(pbftBaseImpl);
// 启动共识
consensus.start(param);
logger.info("consensus service start success");
}

Dpos 中加载

//TODO