Skip to content

Commit a41321c

Browse files
authored
fix(net): exit solidity node block sync on shutdown (tronprotocol#6804)
1 parent a771d44 commit a41321c

6 files changed

Lines changed: 209 additions & 137 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ The TRON network is mainly divided into:
109109
- **Private Networks**
110110
Customized TRON networks set up by private entities for testing, development, or specific use cases.
111111

112-
Network selection is performed by specifying the appropriate configuration file upon full-node startup. Mainnet configuration: [config.conf](framework/src/main/resources/config.conf); Nile testnet configuration: [config-nile.conf](https://github.com/tron-nile-testnet/nile-testnet/blob/master/framework/src/main/resources/config-nile.conf)
112+
Network selection is performed by specifying the appropriate configuration file upon full-node startup. Built-in configuration template: [reference.conf](common/src/main/resources/reference.conf); Mainnet configuration: [config.conf](framework/src/main/resources/config.conf); Nile testnet configuration: [config-nile.conf](https://github.com/tron-nile-testnet/nile-testnet/blob/master/framework/src/main/resources/config-nile.conf)
113113

114114
### 1. Join the TRON main network
115115
Launch a main-network full node with the built-in default configuration:

common/src/main/resources/reference.conf

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,17 +25,6 @@
2525
# Key naming rules (required for ConfigBeanFactory auto-binding):
2626
# - Use standard camelCase: maxConnections, syncFetchBatchNum, etc.
2727
#
28-
# Keys that cannot auto-bind (handled via normalizeNonStandardKeys() or manual reads):
29-
#
30-
# 1. committee.pBFTExpireNum / committee.allowPBFT — normalized to camelCase in
31-
# CommitteeConfig.normalizeNonStandardKeys() before ConfigBeanFactory binding.
32-
#
33-
# 2. node.isOpenFullTcpDisconnect — normalized to "openFullTcpDisconnect" in
34-
# NodeConfig.normalizeNonStandardKeys() before ConfigBeanFactory binding.
35-
#
36-
# 3. node.shutdown.BlockTime/BlockHeight/BlockCount — optional PascalCase nested keys;
37-
# read manually in NodeConfig.fromConfig() after ConfigBeanFactory binding.
38-
#
3928
# =============================================================================
4029

4130
net {

framework/src/main/java/org/tron/core/net/TronNetDelegate.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,9 @@ public class TronNetDelegate {
111111
@PostConstruct
112112
public void init() {
113113
hitThread = new Thread(() -> {
114-
LockSupport.park();
114+
while (!hitDown && !Thread.currentThread().isInterrupted()) {
115+
LockSupport.park();
116+
}
115117
// to Guarantee Some other thread invokes unpark with the current thread as the target
116118
if (hitDown && exit) {
117119
System.exit(0);

framework/src/main/java/org/tron/program/SolidityNode.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ private void getBlock() {
122122
logger.info("getBlock interrupted, exiting.");
123123
return;
124124
} catch (Exception e) {
125-
if (!flag) {
125+
if (!flag || tronNetDelegate.isHitDown()) {
126126
logger.info("getBlock stopped during shutdown, last block: {}.", blockNum);
127127
return;
128128
}
@@ -185,6 +185,10 @@ private Block getBlockByNum(long blockNum) {
185185
sleep(exceptionSleepTime);
186186
}
187187
} catch (Exception e) {
188+
if (!flag || tronNetDelegate.isHitDown()) {
189+
logger.info("getBlockByNum stopped during shutdown, block: {}.", blockNum);
190+
break;
191+
}
188192
logger.error("Failed to get block: {}, reason: {}.", blockNum, e.getMessage());
189193
sleep(exceptionSleepTime);
190194
}
@@ -202,7 +206,7 @@ private long getLastSolidityBlockNum() {
202206
blockNum, remoteBlockNum, System.currentTimeMillis() - time);
203207
return blockNum;
204208
} catch (Exception e) {
205-
if (!flag) {
209+
if (!flag || tronNetDelegate.isHitDown()) {
206210
logger.info("getLastSolidityBlockNum stopped during shutdown.");
207211
return 0;
208212
}

framework/src/test/java/org/tron/core/zksnark/ShieldedReceiveTest.java

Lines changed: 157 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import com.google.protobuf.Any;
99
import com.google.protobuf.ByteString;
1010
import com.google.protobuf.InvalidProtocolBufferException;
11+
import java.lang.reflect.Field;
1112
import java.security.SignatureException;
1213
import java.util.Arrays;
1314
import java.util.HashSet;
@@ -45,6 +46,8 @@
4546
import org.tron.common.zksnark.LibrustzcashParam.IvkToPkdParams;
4647
import org.tron.common.zksnark.LibrustzcashParam.OutputProofParams;
4748
import org.tron.common.zksnark.LibrustzcashParam.SpendSigParams;
49+
import org.tron.consensus.dpos.DposSlot;
50+
import org.tron.consensus.dpos.DposTask;
4851
import org.tron.core.Wallet;
4952
import org.tron.core.actuator.Actuator;
5053
import org.tron.core.actuator.ActuatorCreator;
@@ -140,14 +143,16 @@ public class ShieldedReceiveTest extends BaseTest {
140143
@Resource
141144
private ConsensusService consensusService;
142145
@Resource
146+
private DposTask dposTask;
147+
@Resource
143148
private Wallet wallet;
144149
@Resource
145-
private TransactionUtil transactionUtil;
150+
private DposSlot dposSlot;
146151

147152
private static boolean init;
148153

149154
static {
150-
Args.setParam(new String[]{"--output-directory", dbPath(), "-w"}, SHIELD_CONF);
155+
Args.setParam(new String[] {"--output-directory", dbPath(), "-w"}, SHIELD_CONF);
151156
ADDRESS_ONE_PRIVATE_KEY = getRandomPrivateKey();
152157
FROM_ADDRESS = getHexAddressByPrivateKey(ADDRESS_ONE_PRIVATE_KEY);
153158
}
@@ -331,7 +336,7 @@ public void testBroadcastBeforeAllowZksnark()
331336

332337
//Add public address sign
333338
transactionCap = TransactionUtils.addTransactionSign(transactionCap.getInstance(),
334-
ADDRESS_ONE_PRIVATE_KEY, chainBaseManager.getAccountStore());
339+
ADDRESS_ONE_PRIVATE_KEY, chainBaseManager.getAccountStore());
335340
try {
336341
dbManager.pushTransaction(transactionCap);
337342
} catch (Exception e) {
@@ -433,7 +438,7 @@ public String[] generateSpendAndOutputParams() throws ZksnarkException, BadItemE
433438
boolean ok2 = JLibrustzcash.librustzcashSaplingCheckOutput(checkOutputParams);
434439
Assert.assertTrue(ok2);
435440

436-
return new String[]{ByteArray.toHexString(checkSpendParamsData),
441+
return new String[] {ByteArray.toHexString(checkSpendParamsData),
437442
ByteArray.toHexString(dataToBeSigned),
438443
ByteArray.toHexString(checkOutputParams.encode())};
439444
}
@@ -2402,128 +2407,158 @@ public void pushSameSkAndScanAndSpend() throws Exception {
24022407
assert ecKey != null;
24032408
byte[] witnessAddress = ecKey.getAddress();
24042409
WitnessCapsule witnessCapsule = new WitnessCapsule(ByteString.copyFrom(witnessAddress));
2405-
chainBaseManager.addWitness(ByteString.copyFrom(witnessAddress));
2406-
2407-
//sometimes generate block failed, try several times.
2408-
long time = System.currentTimeMillis();
2409-
Block block = getSignedBlock(witnessCapsule.getAddress(), time, privateKey);
2410-
dbManager.pushBlock(new BlockCapsule(block));
2411-
2412-
//create transactions
2413-
chainBaseManager.getDynamicPropertiesStore().saveAllowShieldedTransaction(1);
2414-
chainBaseManager.getDynamicPropertiesStore().saveTotalShieldedPoolValue(1000 * 1000000L);
2415-
ZenTransactionBuilder builder = new ZenTransactionBuilder(wallet);
2416-
2417-
// generate spend proof
2418-
SpendingKey sk = SpendingKey
2419-
.decode("ff2c06269315333a9207f817d2eca0ac555ca8f90196976324c7756504e7c9ee");
2420-
ExpandedSpendingKey expsk = sk.expandedSpendingKey();
2421-
byte[] senderOvk = expsk.getOvk();
2422-
PaymentAddress address = sk.defaultAddress();
2423-
Note note = new Note(address, 1000 * 1000000L);
2424-
IncrementalMerkleVoucherContainer voucher = createSimpleMerkleVoucherContainer(note.cm());
2425-
byte[] anchor = voucher.root().getContent().toByteArray();
2426-
chainBaseManager.getMerkleContainer()
2427-
.putMerkleTreeIntoStore(anchor, voucher.getVoucherCapsule().getTree());
2428-
builder.addSpend(expsk, note, anchor, voucher);
2429-
2430-
// generate output proof
2431-
SpendingKey sk2 = SpendingKey.random();
2432-
FullViewingKey fullViewingKey = sk2.fullViewingKey();
2433-
IncomingViewingKey incomingViewingKey = fullViewingKey.inViewingKey();
2434-
2435-
byte[] memo = org.tron.keystore.Wallet.generateRandomBytes(512);
2436-
2437-
//send coin to 2 different address generated by same sk
2438-
DiversifierT d1 = DiversifierT.random();
2439-
PaymentAddress paymentAddress1 = incomingViewingKey.address(d1).get();
2440-
builder.addOutput(senderOvk, paymentAddress1,
2441-
(1000 * 1000000L - wallet.getShieldedTransactionFee()) / 2, memo);
2442-
2443-
DiversifierT d2 = DiversifierT.random();
2444-
PaymentAddress paymentAddress2 = incomingViewingKey.address(d2).get();
2445-
builder.addOutput(senderOvk, paymentAddress2,
2446-
(1000 * 1000000L - wallet.getShieldedTransactionFee()) / 2, memo);
2447-
2448-
TransactionCapsule transactionCap = builder.build();
2410+
// Stop the consensus task before modifying the witness schedule: DposTask uses the same
2411+
// localwitness key and would otherwise race to produce blocks at the same slot,
2412+
// triggering fork resolution and making the test slow.
2413+
consensusService.stop();
2414+
try {
2415+
chainBaseManager.addWitness(ByteString.copyFrom(witnessAddress));
2416+
2417+
long time = nextScheduledTime(witnessCapsule.getAddress());
2418+
Block block = getSignedBlock(witnessCapsule.getAddress(), time, privateKey);
2419+
dbManager.pushBlock(new BlockCapsule(block));
2420+
2421+
//create transactions
2422+
chainBaseManager.getDynamicPropertiesStore().saveAllowShieldedTransaction(1);
2423+
chainBaseManager.getDynamicPropertiesStore().saveTotalShieldedPoolValue(1000 * 1000000L);
2424+
ZenTransactionBuilder builder = new ZenTransactionBuilder(wallet);
2425+
2426+
// generate spend proof
2427+
SpendingKey sk = SpendingKey
2428+
.decode("ff2c06269315333a9207f817d2eca0ac555ca8f90196976324c7756504e7c9ee");
2429+
ExpandedSpendingKey expsk = sk.expandedSpendingKey();
2430+
byte[] senderOvk = expsk.getOvk();
2431+
PaymentAddress address = sk.defaultAddress();
2432+
Note note = new Note(address, 1000 * 1000000L);
2433+
IncrementalMerkleVoucherContainer voucher = createSimpleMerkleVoucherContainer(note.cm());
2434+
byte[] anchor = voucher.root().getContent().toByteArray();
2435+
chainBaseManager.getMerkleContainer()
2436+
.putMerkleTreeIntoStore(anchor, voucher.getVoucherCapsule().getTree());
2437+
builder.addSpend(expsk, note, anchor, voucher);
2438+
2439+
// generate output proof
2440+
SpendingKey sk2 = SpendingKey.random();
2441+
FullViewingKey fullViewingKey = sk2.fullViewingKey();
2442+
IncomingViewingKey incomingViewingKey = fullViewingKey.inViewingKey();
2443+
2444+
byte[] memo = org.tron.keystore.Wallet.generateRandomBytes(512);
2445+
2446+
//send coin to 2 different address generated by same sk
2447+
DiversifierT d1 = DiversifierT.random();
2448+
PaymentAddress paymentAddress1 = incomingViewingKey.address(d1).get();
2449+
builder.addOutput(senderOvk, paymentAddress1,
2450+
(1000 * 1000000L - wallet.getShieldedTransactionFee()) / 2, memo);
2451+
2452+
DiversifierT d2 = DiversifierT.random();
2453+
PaymentAddress paymentAddress2 = incomingViewingKey.address(d2).get();
2454+
builder.addOutput(senderOvk, paymentAddress2,
2455+
(1000 * 1000000L - wallet.getShieldedTransactionFee()) / 2, memo);
24492456

2450-
byte[] trxId = transactionCap.getTransactionId().getBytes();
2451-
boolean ok = dbManager.pushTransaction(transactionCap);
2452-
Assert.assertTrue(ok);
2457+
TransactionCapsule transactionCap = builder.build();
24532458

2454-
Thread.sleep(500);
2455-
//package transaction to block
2456-
block = getSignedBlock(witnessCapsule.getAddress(), time + 3000, privateKey);
2457-
dbManager.pushBlock(new BlockCapsule(block));
2458-
2459-
BlockCapsule blockCapsule3 = new BlockCapsule(wallet.getNowBlock());
2460-
Assert.assertEquals("blocknum != 2", 2, blockCapsule3.getNum());
2461-
2462-
block = getSignedBlock(witnessCapsule.getAddress(), time + 6000, privateKey);
2463-
dbManager.pushBlock(new BlockCapsule(block));
2464-
2465-
// scan note by ivk
2466-
byte[] receiverIvk = incomingViewingKey.getValue();
2467-
DecryptNotes notes1 = wallet.scanNoteByIvk(0, 100, receiverIvk);
2468-
Assert.assertEquals(2, notes1.getNoteTxsCount());
2469-
2470-
// scan note by ivk and mark
2471-
DecryptNotesMarked notes3 = wallet.scanAndMarkNoteByIvk(0, 100, receiverIvk,
2472-
fullViewingKey.getAk(), fullViewingKey.getNk());
2473-
Assert.assertEquals(2, notes3.getNoteTxsCount());
2474-
2475-
// scan note by ovk
2476-
DecryptNotes notes2 = wallet.scanNoteByOvk(0, 100, senderOvk);
2477-
Assert.assertEquals(2, notes2.getNoteTxsCount());
2478-
2479-
// to spend received note above.
2480-
ZenTransactionBuilder builder2 = new ZenTransactionBuilder(wallet);
2481-
2482-
//query merkleinfo
2483-
OutputPointInfo.Builder request = OutputPointInfo.newBuilder();
2484-
for (int i = 0; i < notes1.getNoteTxsCount(); i++) {
2485-
OutputPoint.Builder outPointBuild = OutputPoint.newBuilder();
2486-
outPointBuild.setHash(ByteString.copyFrom(trxId));
2487-
outPointBuild.setIndex(i);
2488-
request.addOutPoints(outPointBuild.build());
2489-
}
2490-
request.setBlockNum(1);
2491-
IncrementalMerkleVoucherInfo merkleVoucherInfo = wallet
2492-
.getMerkleTreeVoucherInfo(request.build());
2493-
2494-
//build spend proof. allow only one note in spend
2495-
ExpandedSpendingKey expsk2 = sk2.expandedSpendingKey();
2496-
for (int i = 0; i < 1; i++) {
2497-
org.tron.api.GrpcAPI.Note grpcNote = notes1.getNoteTxs(i).getNote();
2498-
PaymentAddress paymentAddress = KeyIo.decodePaymentAddress(grpcNote.getPaymentAddress());
2499-
Note note2 = new Note(paymentAddress.getD(),
2500-
paymentAddress.getPkD(),
2501-
grpcNote.getValue(),
2502-
grpcNote.getRcm().toByteArray()
2503-
);
2459+
byte[] trxId = transactionCap.getTransactionId().getBytes();
2460+
boolean ok = dbManager.pushTransaction(transactionCap);
2461+
Assert.assertTrue(ok);
2462+
2463+
Thread.sleep(500);
2464+
//package transaction to block
2465+
long expectedBlockNum = chainBaseManager.getDynamicPropertiesStore()
2466+
.getLatestBlockHeaderNumber() + 1;
2467+
block = getSignedBlock(witnessCapsule.getAddress(),
2468+
nextScheduledTime(witnessCapsule.getAddress()), privateKey);
2469+
dbManager.pushBlock(new BlockCapsule(block));
2470+
2471+
BlockCapsule blockCapsule3 = new BlockCapsule(wallet.getNowBlock());
2472+
Assert.assertEquals("unexpected block number", expectedBlockNum, blockCapsule3.getNum());
2473+
2474+
block = getSignedBlock(witnessCapsule.getAddress(),
2475+
nextScheduledTime(witnessCapsule.getAddress()), privateKey);
2476+
dbManager.pushBlock(new BlockCapsule(block));
2477+
2478+
// scan note by ivk
2479+
byte[] receiverIvk = incomingViewingKey.getValue();
2480+
DecryptNotes notes1 = wallet.scanNoteByIvk(0, 100, receiverIvk);
2481+
Assert.assertEquals(2, notes1.getNoteTxsCount());
2482+
2483+
// scan note by ivk and mark
2484+
DecryptNotesMarked notes3 = wallet.scanAndMarkNoteByIvk(0, 100, receiverIvk,
2485+
fullViewingKey.getAk(), fullViewingKey.getNk());
2486+
Assert.assertEquals(2, notes3.getNoteTxsCount());
2487+
2488+
// scan note by ovk
2489+
DecryptNotes notes2 = wallet.scanNoteByOvk(0, 100, senderOvk);
2490+
Assert.assertEquals(2, notes2.getNoteTxsCount());
2491+
2492+
// to spend received note above.
2493+
ZenTransactionBuilder builder2 = new ZenTransactionBuilder(wallet);
2494+
2495+
//query merkleinfo
2496+
OutputPointInfo.Builder request = OutputPointInfo.newBuilder();
2497+
for (int i = 0; i < notes1.getNoteTxsCount(); i++) {
2498+
OutputPoint.Builder outPointBuild = OutputPoint.newBuilder();
2499+
outPointBuild.setHash(ByteString.copyFrom(trxId));
2500+
outPointBuild.setIndex(i);
2501+
request.addOutPoints(outPointBuild.build());
2502+
}
2503+
request.setBlockNum(1);
2504+
IncrementalMerkleVoucherInfo merkleVoucherInfo = wallet
2505+
.getMerkleTreeVoucherInfo(request.build());
2506+
2507+
//build spend proof. allow only one note in spend
2508+
ExpandedSpendingKey expsk2 = sk2.expandedSpendingKey();
2509+
for (int i = 0; i < 1; i++) {
2510+
org.tron.api.GrpcAPI.Note grpcNote = notes1.getNoteTxs(i).getNote();
2511+
PaymentAddress paymentAddress = KeyIo.decodePaymentAddress(grpcNote.getPaymentAddress());
2512+
Note note2 = new Note(paymentAddress.getD(),
2513+
paymentAddress.getPkD(),
2514+
grpcNote.getValue(),
2515+
grpcNote.getRcm().toByteArray()
2516+
);
2517+
2518+
IncrementalMerkleVoucherContainer voucher2 =
2519+
new IncrementalMerkleVoucherContainer(
2520+
new IncrementalMerkleVoucherCapsule(merkleVoucherInfo.getVouchers(i)));
2521+
byte[] anchor2 = voucher2.root().getContent().toByteArray();
2522+
builder2.addSpend(expsk2, note2, anchor2, voucher2);
2523+
}
25042524

2505-
IncrementalMerkleVoucherContainer voucher2 =
2506-
new IncrementalMerkleVoucherContainer(
2507-
new IncrementalMerkleVoucherCapsule(merkleVoucherInfo.getVouchers(i)));
2508-
byte[] anchor2 = voucher2.root().getContent().toByteArray();
2509-
builder2.addSpend(expsk2, note2, anchor2, voucher2);
2525+
//build output proof
2526+
SpendingKey sk3 = SpendingKey.random();
2527+
FullViewingKey fvk3 = sk3.fullViewingKey();
2528+
IncomingViewingKey ivk3 = fvk3.inViewingKey();
2529+
2530+
DiversifierT d3 = DiversifierT.random();
2531+
PaymentAddress paymentAddress3 = incomingViewingKey.address(d3).get();
2532+
byte[] memo3 = org.tron.keystore.Wallet.generateRandomBytes(512);
2533+
builder2.addOutput(expsk2.getOvk(), paymentAddress3,
2534+
(1000 * 1000000L - wallet.getShieldedTransactionFee()) / 2 - wallet
2535+
.getShieldedTransactionFee(), memo3);
2536+
2537+
TransactionCapsule transactionCap2 = builder2.build();
2538+
boolean ok2 = dbManager.pushTransaction(transactionCap2);
2539+
Assert.assertTrue(ok2);
2540+
} finally {
2541+
// DposTask.init() does not reset isRunning (it stays false after stop()), so force it back
2542+
// to true via reflection before restarting.
2543+
Field isRunning = DposTask.class.getDeclaredField("isRunning");
2544+
isRunning.setAccessible(true);
2545+
isRunning.set(dposTask, true);
2546+
consensusService.start();
2547+
}
2548+
}
2549+
2550+
// Returns the earliest timestamp at which witnessAddr is the DPoS-scheduled producer,
2551+
// relative to the current chain head. Using this avoids relying on the genesis-only
2552+
// bypass in validBlock() (latestBlockHeaderNumber == 0) when prior tests have pushed blocks.
2553+
private long nextScheduledTime(ByteString witnessAddr) {
2554+
int size = chainBaseManager.getWitnessScheduleStore().getActiveWitnesses().size();
2555+
for (long slot = 1; slot <= size; slot++) {
2556+
if (dposSlot.getScheduledWitness(slot).equals(witnessAddr)) {
2557+
return dposSlot.getTime(slot);
2558+
}
25102559
}
2511-
2512-
//build output proof
2513-
SpendingKey sk3 = SpendingKey.random();
2514-
FullViewingKey fvk3 = sk3.fullViewingKey();
2515-
IncomingViewingKey ivk3 = fvk3.inViewingKey();
2516-
2517-
DiversifierT d3 = DiversifierT.random();
2518-
PaymentAddress paymentAddress3 = incomingViewingKey.address(d3).get();
2519-
byte[] memo3 = org.tron.keystore.Wallet.generateRandomBytes(512);
2520-
builder2.addOutput(expsk2.getOvk(), paymentAddress3,
2521-
(1000 * 1000000L - wallet.getShieldedTransactionFee()) / 2 - wallet
2522-
.getShieldedTransactionFee(), memo3);
2523-
2524-
TransactionCapsule transactionCap2 = builder2.build();
2525-
boolean ok2 = dbManager.pushTransaction(transactionCap2);
2526-
Assert.assertTrue(ok2);
2560+
throw new IllegalStateException("No scheduled slot for witness within "
2561+
+ size + " slots: " + ByteArray.toHexString(witnessAddr.toByteArray()));
25272562
}
25282563

25292564
@Test

0 commit comments

Comments
 (0)