From aa682c92c3832ebd4f21af333b9f742c1c785972 Mon Sep 17 00:00:00 2001 From: chengtx01 Date: Tue, 6 May 2025 18:01:57 +0800 Subject: [PATCH 01/59] feat(net):P2P message rate limit --- .../tron/core/net/P2pEventHandlerImpl.java | 8 +++-- .../org/tron/core/net/P2pRateLimiter.java | 30 +++++++++++++++++++ .../FetchInvDataMsgHandler.java | 11 +++++++ .../SyncBlockChainMsgHandler.java | 7 +++++ .../tron/core/net/peer/PeerConnection.java | 11 +++++++ 5 files changed, 64 insertions(+), 3 deletions(-) create mode 100644 framework/src/main/java/org/tron/core/net/P2pRateLimiter.java diff --git a/framework/src/main/java/org/tron/core/net/P2pEventHandlerImpl.java b/framework/src/main/java/org/tron/core/net/P2pEventHandlerImpl.java index 795c90b4edd..90c3739a3ec 100644 --- a/framework/src/main/java/org/tron/core/net/P2pEventHandlerImpl.java +++ b/framework/src/main/java/org/tron/core/net/P2pEventHandlerImpl.java @@ -178,9 +178,11 @@ private void processMessage(PeerConnection peer, byte[] data) { handshakeService.processHelloMessage(peer, (HelloMessage) msg); break; case P2P_DISCONNECT: - peer.getChannel().close(); - peer.getNodeStatistics() - .nodeDisconnectedRemote(((DisconnectMessage)msg).getReason()); + if (peer.getP2pRateLimiter().tryAcquire(type.asByte())) { + peer.getChannel().close(); + peer.getNodeStatistics() + .nodeDisconnectedRemote(((DisconnectMessage)msg).getReason()); + } break; case SYNC_BLOCK_CHAIN: syncBlockChainMsgHandler.processMessage(peer, msg); diff --git a/framework/src/main/java/org/tron/core/net/P2pRateLimiter.java b/framework/src/main/java/org/tron/core/net/P2pRateLimiter.java new file mode 100644 index 00000000000..aa1c7bbeffc --- /dev/null +++ b/framework/src/main/java/org/tron/core/net/P2pRateLimiter.java @@ -0,0 +1,30 @@ +package org.tron.core.net; + +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.google.common.util.concurrent.RateLimiter; + +public class P2pRateLimiter { + private final Cache rateLimiters = CacheBuilder.newBuilder() + .maximumSize(256).build(); + + public void register (Byte type, double rate) { + rateLimiters.put(type, RateLimiter.create(rate)); + } + + public void acquire (Byte type) { + RateLimiter rateLimiter = rateLimiters.getIfPresent(type); + if (rateLimiter == null) { + return; + } + rateLimiter.acquire(); + } + + public boolean tryAcquire (Byte type) { + RateLimiter rateLimiter = rateLimiters.getIfPresent(type); + if (rateLimiter == null) { + return true; + } + return rateLimiter.tryAcquire(); + } +} diff --git a/framework/src/main/java/org/tron/core/net/messagehandler/FetchInvDataMsgHandler.java b/framework/src/main/java/org/tron/core/net/messagehandler/FetchInvDataMsgHandler.java index 5415ea435e3..7d25b995414 100644 --- a/framework/src/main/java/org/tron/core/net/messagehandler/FetchInvDataMsgHandler.java +++ b/framework/src/main/java/org/tron/core/net/messagehandler/FetchInvDataMsgHandler.java @@ -55,6 +55,13 @@ public void processMessage(PeerConnection peer, TronMessage msg) throws P2pExcep FetchInvDataMessage fetchInvDataMsg = (FetchInvDataMessage) msg; + if (peer.isNeedSyncFromUs() && !peer.getP2pRateLimiter().tryAcquire(msg.getType().asByte())) { + // Discard messages that exceed the rate limit + logger.warn("{} message from peer {} exceeds the rate limit", + msg.getType(), peer.getInetSocketAddress()); + return; + } + check(peer, fetchInvDataMsg); InventoryType type = fetchInvDataMsg.getInventoryType(); @@ -156,6 +163,10 @@ private void check(PeerConnection peer, FetchInvDataMessage fetchInvDataMsg) thr if (!peer.isNeedSyncFromUs()) { throw new P2pException(TypeEnum.BAD_MESSAGE, "no need sync"); } + if (fetchInvDataMsg.getHashList().size() > 100) { + throw new P2pException(TypeEnum.BAD_MESSAGE, "fetch too more blocks, size:" + + fetchInvDataMsg.getHashList().size()); + } for (Sha256Hash hash : fetchInvDataMsg.getHashList()) { long blockNum = new BlockId(hash).getNum(); long minBlockNum = diff --git a/framework/src/main/java/org/tron/core/net/messagehandler/SyncBlockChainMsgHandler.java b/framework/src/main/java/org/tron/core/net/messagehandler/SyncBlockChainMsgHandler.java index 55446593bd0..4ab3c7dd172 100644 --- a/framework/src/main/java/org/tron/core/net/messagehandler/SyncBlockChainMsgHandler.java +++ b/framework/src/main/java/org/tron/core/net/messagehandler/SyncBlockChainMsgHandler.java @@ -31,6 +31,13 @@ public void processMessage(PeerConnection peer, TronMessage msg) throws P2pExcep SyncBlockChainMessage syncBlockChainMessage = (SyncBlockChainMessage) msg; + if (!peer.getP2pRateLimiter().tryAcquire(msg.getType().asByte())) { + // Discard messages that exceed the rate limit + logger.warn("{} message from peer {} exceeds the rate limit", + msg.getType(), peer.getInetSocketAddress()); + return; + } + if (!check(peer, syncBlockChainMessage)) { peer.disconnect(Protocol.ReasonCode.BAD_PROTOCOL); return; diff --git a/framework/src/main/java/org/tron/core/net/peer/PeerConnection.java b/framework/src/main/java/org/tron/core/net/peer/PeerConnection.java index 2e08e105bed..d64dd731873 100644 --- a/framework/src/main/java/org/tron/core/net/peer/PeerConnection.java +++ b/framework/src/main/java/org/tron/core/net/peer/PeerConnection.java @@ -1,5 +1,9 @@ package org.tron.core.net.peer; +import static org.tron.core.net.message.MessageTypes.FETCH_INV_DATA; +import static org.tron.core.net.message.MessageTypes.P2P_DISCONNECT; +import static org.tron.core.net.message.MessageTypes.SYNC_BLOCK_CHAIN; + import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; import com.google.protobuf.ByteString; @@ -32,7 +36,9 @@ import org.tron.core.config.args.Args; import org.tron.core.metrics.MetricsKey; import org.tron.core.metrics.MetricsUtil; +import org.tron.core.net.P2pRateLimiter; import org.tron.core.net.TronNetDelegate; +import org.tron.core.net.message.MessageTypes; import org.tron.core.net.message.adv.InventoryMessage; import org.tron.core.net.message.adv.TransactionsMessage; import org.tron.core.net.message.base.DisconnectMessage; @@ -156,6 +162,8 @@ public class PeerConnection { @Setter @Getter private volatile boolean needSyncFromUs = true; + @Getter + private P2pRateLimiter p2pRateLimiter = new P2pRateLimiter(); public void setChannel(Channel channel) { this.channel = channel; @@ -164,6 +172,9 @@ public void setChannel(Channel channel) { } this.nodeStatistics = TronStatsManager.getNodeStatistics(channel.getInetAddress()); lastInteractiveTime = System.currentTimeMillis(); + p2pRateLimiter.register(SYNC_BLOCK_CHAIN.asByte(), 2); + p2pRateLimiter.register(FETCH_INV_DATA.asByte(), 1); + p2pRateLimiter.register(P2P_DISCONNECT.asByte(), 1); } public void setBlockBothHave(BlockId blockId) { From e50e11f7aaadfd5b4e21e2b01d6546a0d956d4df Mon Sep 17 00:00:00 2001 From: chengtx01 Date: Thu, 15 May 2025 19:17:46 +0800 Subject: [PATCH 02/59] feat(net):test for P2P message rate limit --- .../FetchInvDataMsgHandler.java | 3 +- .../org/tron/core/net/P2pRateLimiterTest.java | 21 ++++++++++++ .../FetchInvDataMsgHandlerTest.java | 34 +++++++++++++++++++ 3 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 framework/src/test/java/org/tron/core/net/P2pRateLimiterTest.java diff --git a/framework/src/main/java/org/tron/core/net/messagehandler/FetchInvDataMsgHandler.java b/framework/src/main/java/org/tron/core/net/messagehandler/FetchInvDataMsgHandler.java index 7d25b995414..3b52b5798c8 100644 --- a/framework/src/main/java/org/tron/core/net/messagehandler/FetchInvDataMsgHandler.java +++ b/framework/src/main/java/org/tron/core/net/messagehandler/FetchInvDataMsgHandler.java @@ -41,6 +41,7 @@ public class FetchInvDataMsgHandler implements TronMsgHandler { .maximumSize(1000).expireAfterWrite(1, TimeUnit.HOURS).build(); private static final int MAX_SIZE = 1_000_000; + private static final int MAX_FETCH_SIZE = 100; @Autowired private TronNetDelegate tronNetDelegate; @Autowired @@ -163,7 +164,7 @@ private void check(PeerConnection peer, FetchInvDataMessage fetchInvDataMsg) thr if (!peer.isNeedSyncFromUs()) { throw new P2pException(TypeEnum.BAD_MESSAGE, "no need sync"); } - if (fetchInvDataMsg.getHashList().size() > 100) { + if (fetchInvDataMsg.getHashList().size() > MAX_FETCH_SIZE) { throw new P2pException(TypeEnum.BAD_MESSAGE, "fetch too more blocks, size:" + fetchInvDataMsg.getHashList().size()); } diff --git a/framework/src/test/java/org/tron/core/net/P2pRateLimiterTest.java b/framework/src/test/java/org/tron/core/net/P2pRateLimiterTest.java new file mode 100644 index 00000000000..8d6a10bb167 --- /dev/null +++ b/framework/src/test/java/org/tron/core/net/P2pRateLimiterTest.java @@ -0,0 +1,21 @@ +package org.tron.core.net; + +import static org.tron.core.net.message.MessageTypes.FETCH_INV_DATA; +import static org.tron.core.net.message.MessageTypes.SYNC_BLOCK_CHAIN; + +import org.junit.Assert; +import org.junit.Test; + +public class P2pRateLimiterTest { + @Test + public void test() { + P2pRateLimiter limiter = new P2pRateLimiter(); + limiter.register(SYNC_BLOCK_CHAIN.asByte(), 2); + limiter.acquire(SYNC_BLOCK_CHAIN.asByte()); + limiter.tryAcquire(SYNC_BLOCK_CHAIN.asByte()); + boolean ret = limiter.tryAcquire(SYNC_BLOCK_CHAIN.asByte()); + Assert.assertFalse(ret); + ret = limiter.tryAcquire(FETCH_INV_DATA.asByte()); + Assert.assertTrue(ret); + } +} diff --git a/framework/src/test/java/org/tron/core/net/messagehandler/FetchInvDataMsgHandlerTest.java b/framework/src/test/java/org/tron/core/net/messagehandler/FetchInvDataMsgHandlerTest.java index 5fd6d6725ba..82be8bc3d5d 100644 --- a/framework/src/test/java/org/tron/core/net/messagehandler/FetchInvDataMsgHandlerTest.java +++ b/framework/src/test/java/org/tron/core/net/messagehandler/FetchInvDataMsgHandlerTest.java @@ -13,6 +13,7 @@ import org.tron.common.utils.Sha256Hash; import org.tron.core.capsule.BlockCapsule; import org.tron.core.config.Parameter; +import org.tron.core.net.P2pRateLimiter; import org.tron.core.net.TronNetDelegate; import org.tron.core.net.message.adv.BlockMessage; import org.tron.core.net.message.adv.FetchInvDataMessage; @@ -21,6 +22,8 @@ import org.tron.core.net.service.adv.AdvService; import org.tron.protos.Protocol; +import static org.tron.core.net.message.MessageTypes.FETCH_INV_DATA; + public class FetchInvDataMsgHandlerTest { @Test @@ -93,4 +96,35 @@ public void testSyncFetchCheck() { Assert.assertEquals(e.getMessage(), "minBlockNum: 16000, blockNum: 10000"); } } + + @Test + public void testRateLimiter() { + BlockCapsule.BlockId blockId = new BlockCapsule.BlockId(Sha256Hash.ZERO_HASH, 10000L); + List blockIds = new LinkedList<>(); + for (int i = 0; i <= 100; i++) { + blockIds.add(blockId); + } + FetchInvDataMessage msg = + new FetchInvDataMessage(blockIds, Protocol.Inventory.InventoryType.BLOCK); + PeerConnection peer = Mockito.mock(PeerConnection.class); + Mockito.when(peer.isNeedSyncFromUs()).thenReturn(true); + Cache advInvSpread = CacheBuilder.newBuilder().maximumSize(100) + .expireAfterWrite(1, TimeUnit.HOURS).recordStats().build(); + Mockito.when(peer.getAdvInvSpread()).thenReturn(advInvSpread); + P2pRateLimiter p2pRateLimiter = new P2pRateLimiter(); + p2pRateLimiter.register(FETCH_INV_DATA.asByte(), 1); + Mockito.when(peer.getP2pRateLimiter()).thenReturn(p2pRateLimiter); + FetchInvDataMsgHandler fetchInvDataMsgHandler = new FetchInvDataMsgHandler(); + + try { + fetchInvDataMsgHandler.processMessage(peer, msg); + } catch (Exception e) { + Assert.assertEquals("fetch too more blocks, size:101", e.getMessage()); + } + try { + fetchInvDataMsgHandler.processMessage(peer, msg); + } catch (Exception e) { + Assert.assertTrue(e.getMessage().endsWith("rate limit")); + } + } } From 618b3527af7e84fb5b5567cedae69e3495324f36 Mon Sep 17 00:00:00 2001 From: chengtx01 Date: Mon, 26 May 2025 15:45:26 +0800 Subject: [PATCH 03/59] feat(net):fix for ambiguous reason code in disconnect messages --- .../tron/core/net/P2pEventHandlerImpl.java | 1 + .../net/message/handshake/HelloMessage.java | 7 +-- .../service/handshake/HandshakeService.java | 2 +- .../net/services/HandShakeServiceTest.java | 54 +++++++++++++++++-- 4 files changed, 55 insertions(+), 9 deletions(-) diff --git a/framework/src/main/java/org/tron/core/net/P2pEventHandlerImpl.java b/framework/src/main/java/org/tron/core/net/P2pEventHandlerImpl.java index 795c90b4edd..181ddbe202a 100644 --- a/framework/src/main/java/org/tron/core/net/P2pEventHandlerImpl.java +++ b/framework/src/main/java/org/tron/core/net/P2pEventHandlerImpl.java @@ -253,6 +253,7 @@ private void processException(PeerConnection peer, TronMessage msg, Exception ex code = Protocol.ReasonCode.BAD_TX; break; case BAD_BLOCK: + case BLOCK_SIGN_ERROR: code = Protocol.ReasonCode.BAD_BLOCK; break; case NO_SUCH_MESSAGE: diff --git a/framework/src/main/java/org/tron/core/net/message/handshake/HelloMessage.java b/framework/src/main/java/org/tron/core/net/message/handshake/HelloMessage.java index 867ced5dbff..68123c93db6 100755 --- a/framework/src/main/java/org/tron/core/net/message/handshake/HelloMessage.java +++ b/framework/src/main/java/org/tron/core/net/message/handshake/HelloMessage.java @@ -5,6 +5,7 @@ import org.apache.commons.lang3.StringUtils; import org.tron.common.utils.ByteArray; import org.tron.common.utils.DecodeUtil; +import org.tron.common.utils.Sha256Hash; import org.tron.common.utils.StringUtil; import org.tron.core.ChainBaseManager; import org.tron.core.capsule.BlockCapsule; @@ -156,17 +157,17 @@ public Protocol.HelloMessage getInstance() { public boolean valid() { byte[] genesisBlockByte = this.helloMessage.getGenesisBlockId().getHash().toByteArray(); - if (genesisBlockByte.length == 0) { + if (genesisBlockByte.length != Sha256Hash.LENGTH) { return false; } byte[] solidBlockId = this.helloMessage.getSolidBlockId().getHash().toByteArray(); - if (solidBlockId.length == 0) { + if (solidBlockId.length != Sha256Hash.LENGTH) { return false; } byte[] headBlockId = this.helloMessage.getHeadBlockId().getHash().toByteArray(); - if (headBlockId.length == 0) { + if (headBlockId.length != Sha256Hash.LENGTH) { return false; } diff --git a/framework/src/main/java/org/tron/core/net/service/handshake/HandshakeService.java b/framework/src/main/java/org/tron/core/net/service/handshake/HandshakeService.java index 6cd117c83dd..098175c4109 100644 --- a/framework/src/main/java/org/tron/core/net/service/handshake/HandshakeService.java +++ b/framework/src/main/java/org/tron/core/net/service/handshake/HandshakeService.java @@ -57,7 +57,7 @@ public void processHelloMessage(PeerConnection peer, HelloMessage msg) { msg.getInstance().getAddress().toByteArray().length, msg.getInstance().getSignature().toByteArray().length, msg.getInstance().getCodeVersion().toByteArray().length); - peer.disconnect(ReasonCode.UNEXPECTED_IDENTITY); + peer.disconnect(ReasonCode.INCOMPATIBLE_PROTOCOL); return; } diff --git a/framework/src/test/java/org/tron/core/net/services/HandShakeServiceTest.java b/framework/src/test/java/org/tron/core/net/services/HandShakeServiceTest.java index f4fabce5d64..8e9a3fa7166 100644 --- a/framework/src/test/java/org/tron/core/net/services/HandShakeServiceTest.java +++ b/framework/src/test/java/org/tron/core/net/services/HandShakeServiceTest.java @@ -21,7 +21,6 @@ import org.mockito.Mockito; import org.springframework.context.ApplicationContext; import org.tron.common.application.TronApplicationContext; -import org.tron.common.utils.ByteArray; import org.tron.common.utils.ReflectUtils; import org.tron.common.utils.Sha256Hash; import org.tron.core.ChainBaseManager; @@ -34,6 +33,7 @@ import org.tron.core.net.message.handshake.HelloMessage; import org.tron.core.net.peer.PeerConnection; import org.tron.core.net.peer.PeerManager; +import org.tron.core.net.service.handshake.HandshakeService; import org.tron.p2p.P2pConfig; import org.tron.p2p.base.Parameter; import org.tron.p2p.connection.Channel; @@ -126,13 +126,27 @@ public void testInvalidHelloMessage() { //block hash is empty try { BlockCapsule.BlockId hid = ChainBaseManager.getChainBaseManager().getHeadBlockId(); - Protocol.HelloMessage.BlockId hBlockId = Protocol.HelloMessage.BlockId.newBuilder() - .setHash(ByteString.copyFrom(new byte[0])) + Protocol.HelloMessage.BlockId okBlockId = Protocol.HelloMessage.BlockId.newBuilder() + .setHash(ByteString.copyFrom(new byte[32])) .setNumber(hid.getNum()) .build(); - builder.setHeadBlockId(hBlockId); + Protocol.HelloMessage.BlockId invalidBlockId = Protocol.HelloMessage.BlockId.newBuilder() + .setHash(ByteString.copyFrom(new byte[31])) + .setNumber(hid.getNum()) + .build(); + builder.setHeadBlockId(invalidBlockId); HelloMessage helloMessage = new HelloMessage(builder.build().toByteArray()); - Assert.assertTrue(!helloMessage.valid()); + Assert.assertFalse(helloMessage.valid()); + + builder.setHeadBlockId(okBlockId); + builder.setGenesisBlockId(invalidBlockId); + HelloMessage helloMessage2 = new HelloMessage(builder.build().toByteArray()); + Assert.assertFalse(helloMessage2.valid()); + + builder.setGenesisBlockId(okBlockId); + builder.setSolidBlockId(invalidBlockId); + HelloMessage helloMessage3 = new HelloMessage(builder.build().toByteArray()); + Assert.assertFalse(helloMessage3.valid()); } catch (Exception e) { Assert.fail(); } @@ -264,6 +278,36 @@ public void testLowAndGenesisBlockNum() throws NoSuchMethodException { } } + @Test + public void testProcessHelloMessage() { + InetSocketAddress a1 = new InetSocketAddress("127.0.0.1", 10001); + Channel c1 = mock(Channel.class); + Mockito.when(c1.getInetSocketAddress()).thenReturn(a1); + Mockito.when(c1.getInetAddress()).thenReturn(a1.getAddress()); + PeerManager.add(ctx, c1); + PeerConnection p = PeerManager.getPeers().get(0); + + try { + Node node = new Node(NetUtil.getNodeId(), a1.getAddress().getHostAddress(), + null, a1.getPort()); + Protocol.HelloMessage.Builder builder = + getHelloMessageBuilder(node, System.currentTimeMillis(), + ChainBaseManager.getChainBaseManager()); + BlockCapsule.BlockId hid = ChainBaseManager.getChainBaseManager().getHeadBlockId(); + Protocol.HelloMessage.BlockId invalidBlockId = Protocol.HelloMessage.BlockId.newBuilder() + .setHash(ByteString.copyFrom(new byte[31])) + .setNumber(hid.getNum()) + .build(); + builder.setHeadBlockId(invalidBlockId); + + HelloMessage helloMessage = new HelloMessage(builder.build().toByteArray()); + HandshakeService handshakeService = new HandshakeService(); + handshakeService.processHelloMessage(p, helloMessage); + } catch (Exception e) { + Assert.fail(); + } + } + private Protocol.HelloMessage.Builder getHelloMessageBuilder(Node from, long timestamp, ChainBaseManager chainBaseManager) { Endpoint fromEndpoint = getEndpointFromNode(from); From 436ab693b3360dcccc7dc423e09d3681eff3cfb6 Mon Sep 17 00:00:00 2001 From: zeusoo001 Date: Fri, 20 Jun 2025 11:39:15 +0800 Subject: [PATCH 04/59] optimize the log for configuration without Blackhole account --- .../org/tron/core/store/AccountStore.java | 4 ++++ .../org/tron/common/exit/ExitManager.java | 2 +- .../org/tron/core/db/AccountStoreTest.java | 24 +++++++++++++++++++ .../core/zksnark/ShieldedReceiveTest.java | 3 ++- 4 files changed, 31 insertions(+), 2 deletions(-) diff --git a/chainbase/src/main/java/org/tron/core/store/AccountStore.java b/chainbase/src/main/java/org/tron/core/store/AccountStore.java index 4d39049ee79..198b5a5129a 100644 --- a/chainbase/src/main/java/org/tron/core/store/AccountStore.java +++ b/chainbase/src/main/java/org/tron/core/store/AccountStore.java @@ -12,6 +12,7 @@ import org.tron.core.capsule.BlockCapsule; import org.tron.core.db.TronStoreWithRevoking; import org.tron.core.db.accountstate.AccountStateCallBackUtils; +import org.tron.core.exception.TronError; import org.tron.protos.contract.BalanceContract.TransactionBalanceTrace; import org.tron.protos.contract.BalanceContract.TransactionBalanceTrace.Operation; @@ -50,6 +51,9 @@ public static void setAccount(com.typesafe.config.Config config) { byte[] address = Commons.decodeFromBase58Check(obj.get("address").unwrapped().toString()); assertsAddress.put(accountName, address); } + if (assertsAddress.get("Blackhole") == null) { + throw new TronError("Account[Blackhole] is not configured.", TronError.ErrCode.GENESIS_BLOCK_INIT); + } } @Override diff --git a/common/src/main/java/org/tron/common/exit/ExitManager.java b/common/src/main/java/org/tron/common/exit/ExitManager.java index d80b7838c08..1a1cc9ed3fc 100644 --- a/common/src/main/java/org/tron/common/exit/ExitManager.java +++ b/common/src/main/java/org/tron/common/exit/ExitManager.java @@ -46,7 +46,7 @@ public static Optional findTronError(Throwable e) { public static void logAndExit(TronError exit) { final int code = exit.getErrCode().getCode(); - logger.error("Shutting down with code: {}.", exit.getErrCode(), exit); + logger.error("Shutting down with code: {}, reason: {}", exit.getErrCode(), exit.getMessage()); Thread exitThread = exitThreadFactory.newThread(() -> System.exit(code)); exitThread.start(); } diff --git a/framework/src/test/java/org/tron/core/db/AccountStoreTest.java b/framework/src/test/java/org/tron/core/db/AccountStoreTest.java index 9249a3358dc..aab64df16c7 100755 --- a/framework/src/test/java/org/tron/core/db/AccountStoreTest.java +++ b/framework/src/test/java/org/tron/core/db/AccountStoreTest.java @@ -1,20 +1,29 @@ package org.tron.core.db; import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; import com.google.protobuf.ByteString; +import com.typesafe.config.Config; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; import javax.annotation.Resource; import org.junit.Assert; import org.junit.Before; import org.junit.Test; +import org.mockito.Mockito; import org.tron.common.BaseTest; import org.tron.common.utils.ByteArray; import org.tron.core.Constant; import org.tron.core.capsule.AccountCapsule; import org.tron.core.config.args.Args; import org.tron.core.db2.ISession; +import org.tron.core.exception.TronError; +import org.tron.core.net.peer.PeerManager; import org.tron.core.store.AccountStore; import org.tron.core.store.AssetIssueStore; import org.tron.core.store.DynamicPropertiesStore; @@ -61,6 +70,21 @@ public void init() { init = true; } + @Test + public void setAccountTest() throws Exception { + Field field = AccountStore.class.getDeclaredField("assertsAddress"); + field.setAccessible(true); + field.set(AccountStore.class, new HashMap<>()); + Config config = mock(Config.class); + Mockito.when(config.getObjectList("genesis.block.assets")).thenReturn(new ArrayList<>()); + try { + AccountStore.setAccount(config); + Assert.fail(); + } catch (Throwable e) { + Assert.assertTrue(e instanceof TronError); + } + } + @Test public void get() { //test get and has Method diff --git a/framework/src/test/java/org/tron/core/zksnark/ShieldedReceiveTest.java b/framework/src/test/java/org/tron/core/zksnark/ShieldedReceiveTest.java index 2a7545f7a9b..0fb5e7b0a97 100755 --- a/framework/src/test/java/org/tron/core/zksnark/ShieldedReceiveTest.java +++ b/framework/src/test/java/org/tron/core/zksnark/ShieldedReceiveTest.java @@ -320,7 +320,8 @@ public void testBroadcastBeforeAllowZksnark() } /* - * generate spendproof, dataToBeSigned, outputproof example dynamically according to the params file + * generate spendproof, dataToBeSigned, outputproof example dynamically according to + * the params file. */ public String[] generateSpendAndOutputParams() throws ZksnarkException, BadItemException { librustzcashInitZksnarkParams(); From b8702a50c533d80e4475fa9f0ca2fd9bdf31b602 Mon Sep 17 00:00:00 2001 From: zeusoo001 Date: Tue, 24 Jun 2025 12:27:05 +0800 Subject: [PATCH 05/59] solve sonar problem --- .../src/main/java/org/tron/core/store/AccountStore.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/chainbase/src/main/java/org/tron/core/store/AccountStore.java b/chainbase/src/main/java/org/tron/core/store/AccountStore.java index 198b5a5129a..5aec5958729 100644 --- a/chainbase/src/main/java/org/tron/core/store/AccountStore.java +++ b/chainbase/src/main/java/org/tron/core/store/AccountStore.java @@ -24,6 +24,8 @@ @Component public class AccountStore extends TronStoreWithRevoking { + private static String ACCOUNT_BLACKHOLE = "Blackhole"; + private static Map assertsAddress = new HashMap<>(); // key = name , value = address @Autowired @@ -51,7 +53,7 @@ public static void setAccount(com.typesafe.config.Config config) { byte[] address = Commons.decodeFromBase58Check(obj.get("address").unwrapped().toString()); assertsAddress.put(accountName, address); } - if (assertsAddress.get("Blackhole") == null) { + if (assertsAddress.get(ACCOUNT_BLACKHOLE) == null) { throw new TronError("Account[Blackhole] is not configured.", TronError.ErrCode.GENESIS_BLOCK_INIT); } } @@ -113,12 +115,12 @@ public AccountCapsule getSun() { * Min TRX account. */ public AccountCapsule getBlackhole() { - return getUnchecked(assertsAddress.get("Blackhole")); + return getUnchecked(assertsAddress.get(ACCOUNT_BLACKHOLE)); } public byte[] getBlackholeAddress() { - return assertsAddress.get("Blackhole"); + return assertsAddress.get(ACCOUNT_BLACKHOLE); } /** From 5304d55a92e44f1e0aad5b86fb7d919ad7bdae81 Mon Sep 17 00:00:00 2001 From: zeusoo001 Date: Tue, 24 Jun 2025 12:22:38 +0800 Subject: [PATCH 06/59] feat(net): add volatile modifier to peer attribute variables --- .../java/org/tron/core/net/peer/PeerConnection.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/framework/src/main/java/org/tron/core/net/peer/PeerConnection.java b/framework/src/main/java/org/tron/core/net/peer/PeerConnection.java index 2e08e105bed..47b199b42bd 100644 --- a/framework/src/main/java/org/tron/core/net/peer/PeerConnection.java +++ b/framework/src/main/java/org/tron/core/net/peer/PeerConnection.java @@ -85,7 +85,7 @@ public class PeerConnection { @Getter @Setter - private TronState tronState = TronState.INIT; + private volatile TronState tronState = TronState.INIT; @Autowired private TronNetDelegate tronNetDelegate; @@ -123,15 +123,15 @@ public class PeerConnection { private Map advInvRequest = new ConcurrentHashMap<>(); @Setter - private BlockId fastForwardBlock; + private volatile BlockId fastForwardBlock; @Getter - private BlockId blockBothHave = new BlockId(); + private volatile BlockId blockBothHave = new BlockId(); @Getter private volatile long blockBothHaveUpdateTime = System.currentTimeMillis(); @Setter @Getter - private BlockId lastSyncBlockId; + private volatile BlockId lastSyncBlockId; @Setter @Getter private volatile long remainNum; @@ -146,7 +146,7 @@ public class PeerConnection { private Map syncBlockRequested = new ConcurrentHashMap<>(); @Setter @Getter - private Pair, Long> syncChainRequested = null; + private volatile Pair, Long> syncChainRequested = null; @Setter @Getter private Set syncBlockInProcess = new HashSet<>(); From 23543e6e12437319d9820829d1e5913d35dbf436 Mon Sep 17 00:00:00 2001 From: yingying Date: Fri, 27 Jun 2025 19:42:53 +0800 Subject: [PATCH 07/59] update readme for telegram groups and doc link --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0d0eeb6ef71..19f43ab73ab 100644 --- a/README.md +++ b/README.md @@ -173,7 +173,7 @@ This will mount the `output-directory` and `logs` directories on the host, the d [Tron Developers & SRs](https://discord.gg/hqKvyAM) is Tron's official Discord channel. Feel free to join this channel if you have any questions. -[Core Devs Community](https://t.me/troncoredevscommunity) is the Telegram channel for java-tron community developers. If you want to contribute to java-tron, please join this channel. +The [Core Devs Community](https://t.me/troncoredevscommunity) and [Tron Official Developer Group](https://t.me/TronOfficialDevelopersGroupEn) are Telegram channels specifically designed for java-tron community developers to engage in technical discussions. [tronprotocol/allcoredev](https://gitter.im/tronprotocol/allcoredev) is the official Gitter channel for developers. @@ -184,7 +184,7 @@ Thank you for considering to help out with the source code! If you'd like to con # Resources - [Medium](https://medium.com/@coredevs) java-tron's official technical articles are published there. -- [Documentation](https://tronprotocol.github.io/documentation-en/introduction/) java-tron's official technical documentation website. +- [Documentation](https://tronprotocol.github.io/documentation-en/) and [TRON Developer Hub](https://developers.tron.network/) serve as java-tron’s primary documentation websites. - [Test network](http://nileex.io/) A stable test network of TRON contributed by TRON community. - [Tronscan](https://tronscan.org/#/) TRON network blockchain browser. - [Wallet-cli](https://github.com/tronprotocol/wallet-cli) TRON network wallet using command line. From 5910b51164643b9ecbe90ae367ebaa2639b6b834 Mon Sep 17 00:00:00 2001 From: halibobo1205 Date: Wed, 4 Jun 2025 16:26:58 +0800 Subject: [PATCH 08/59] feat(doc): update expired information for readme --- README.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/README.md b/README.md index 0d0eeb6ef71..5c8fefccff2 100644 --- a/README.md +++ b/README.md @@ -12,11 +12,7 @@

- - - - - + From b0cf0d4ff015e64ba96e2f845a4d129cb07572ca Mon Sep 17 00:00:00 2001 From: zeusoo001 Date: Thu, 3 Jul 2025 11:59:32 +0800 Subject: [PATCH 09/59] feat(net): optimize the log of synchronization failure --- .../java/org/tron/core/net/service/sync/SyncService.java | 6 +++++- .../java/org/tron/core/net/services/SyncServiceTest.java | 8 ++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/framework/src/main/java/org/tron/core/net/service/sync/SyncService.java b/framework/src/main/java/org/tron/core/net/service/sync/SyncService.java index e387329c467..7281be8d7b4 100644 --- a/framework/src/main/java/org/tron/core/net/service/sync/SyncService.java +++ b/framework/src/main/java/org/tron/core/net/service/sync/SyncService.java @@ -124,7 +124,11 @@ public void syncNext(PeerConnection peer) { peer.setSyncChainRequested(new Pair<>(chainSummary, System.currentTimeMillis())); peer.sendMessage(new SyncBlockChainMessage(chainSummary)); } catch (Exception e) { - logger.error("Peer {} sync failed, reason: {}", peer.getInetAddress(), e); + if (e instanceof P2pException) { + logger.warn("Peer {} sync failed, reason: {}", peer.getInetAddress(), e.getMessage()); + } else { + logger.error("Peer {} sync failed.", peer.getInetAddress(), e); + } peer.disconnect(ReasonCode.SYNC_FAIL); } } diff --git a/framework/src/test/java/org/tron/core/net/services/SyncServiceTest.java b/framework/src/test/java/org/tron/core/net/services/SyncServiceTest.java index c2883fb349d..e5b111a7abe 100644 --- a/framework/src/test/java/org/tron/core/net/services/SyncServiceTest.java +++ b/framework/src/test/java/org/tron/core/net/services/SyncServiceTest.java @@ -19,6 +19,7 @@ import org.springframework.context.ApplicationContext; import org.tron.common.application.TronApplicationContext; import org.tron.common.utils.ReflectUtils; +import org.tron.common.utils.Sha256Hash; import org.tron.core.Constant; import org.tron.core.capsule.BlockCapsule; import org.tron.core.config.DefaultConfig; @@ -91,6 +92,13 @@ public void testStartSync() { ReflectUtils.setFieldValue(peer, "tronState", TronState.INIT); + try { + peer.setBlockBothHave(new BlockCapsule.BlockId(Sha256Hash.ZERO_HASH, -1)); + service.syncNext(peer); + } catch (Exception e) { + // no need to deal with + } + service.startSync(peer); } catch (Exception e) { // no need to deal with From 017207e326c3a56cee9bd4abfd6efacf53d05677 Mon Sep 17 00:00:00 2001 From: zeusoo001 Date: Thu, 3 Jul 2025 14:19:58 +0800 Subject: [PATCH 10/59] feat(net): adjust the order of field assignment to solve concurrency issues --- .../java/org/tron/core/net/service/sync/SyncService.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/framework/src/main/java/org/tron/core/net/service/sync/SyncService.java b/framework/src/main/java/org/tron/core/net/service/sync/SyncService.java index e387329c467..95bdf689bec 100644 --- a/framework/src/main/java/org/tron/core/net/service/sync/SyncService.java +++ b/framework/src/main/java/org/tron/core/net/service/sync/SyncService.java @@ -159,9 +159,8 @@ private void invalid(BlockId blockId, PeerConnection peerConnection) { } private LinkedList getBlockChainSummary(PeerConnection peer) throws P2pException { - - BlockId beginBlockId = peer.getBlockBothHave(); List blockIds = new ArrayList<>(peer.getSyncBlockToFetch()); + BlockId beginBlockId = peer.getBlockBothHave(); List forkList = new LinkedList<>(); LinkedList summary = new LinkedList<>(); long syncBeginNumber = tronNetDelegate.getSyncBeginNumber(); @@ -323,9 +322,9 @@ private void processSyncBlock(BlockCapsule block, PeerConnection peerConnection) for (PeerConnection peer : tronNetDelegate.getActivePeer()) { BlockId bid = peer.getSyncBlockToFetch().peek(); if (blockId.equals(bid)) { + peer.setBlockBothHave(blockId); peer.getSyncBlockToFetch().remove(bid); if (flag) { - peer.setBlockBothHave(blockId); if (peer.getSyncBlockToFetch().isEmpty() && peer.isFetchAble()) { syncNext(peer); } From c2adb7dd07a23d4d6196bcc7bc465d9fae160415 Mon Sep 17 00:00:00 2001 From: chengtx01 Date: Tue, 8 Jul 2025 17:16:28 +0800 Subject: [PATCH 11/59] fix(net):fix P2P message rate limit issue --- .../main/java/org/tron/core/net/P2pRateLimiter.java | 12 +++++++----- .../java/org/tron/core/net/peer/PeerConnection.java | 2 +- .../java/org/tron/core/net/P2pRateLimiterTest.java | 4 +++- .../messagehandler/FetchInvDataMsgHandlerTest.java | 4 ++-- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/framework/src/main/java/org/tron/core/net/P2pRateLimiter.java b/framework/src/main/java/org/tron/core/net/P2pRateLimiter.java index aa1c7bbeffc..9b36e1e5df3 100644 --- a/framework/src/main/java/org/tron/core/net/P2pRateLimiter.java +++ b/framework/src/main/java/org/tron/core/net/P2pRateLimiter.java @@ -6,13 +6,15 @@ public class P2pRateLimiter { private final Cache rateLimiters = CacheBuilder.newBuilder() - .maximumSize(256).build(); + .maximumSize(32).build(); - public void register (Byte type, double rate) { - rateLimiters.put(type, RateLimiter.create(rate)); + public void register(Byte type, double rate) { + RateLimiter rateLimiter = RateLimiter.create(Double.POSITIVE_INFINITY); + rateLimiter.setRate(rate); + rateLimiters.put(type, rateLimiter); } - public void acquire (Byte type) { + public void acquire(Byte type) { RateLimiter rateLimiter = rateLimiters.getIfPresent(type); if (rateLimiter == null) { return; @@ -20,7 +22,7 @@ public void acquire (Byte type) { rateLimiter.acquire(); } - public boolean tryAcquire (Byte type) { + public boolean tryAcquire(Byte type) { RateLimiter rateLimiter = rateLimiters.getIfPresent(type); if (rateLimiter == null) { return true; diff --git a/framework/src/main/java/org/tron/core/net/peer/PeerConnection.java b/framework/src/main/java/org/tron/core/net/peer/PeerConnection.java index d64dd731873..b39f10494c4 100644 --- a/framework/src/main/java/org/tron/core/net/peer/PeerConnection.java +++ b/framework/src/main/java/org/tron/core/net/peer/PeerConnection.java @@ -173,7 +173,7 @@ public void setChannel(Channel channel) { this.nodeStatistics = TronStatsManager.getNodeStatistics(channel.getInetAddress()); lastInteractiveTime = System.currentTimeMillis(); p2pRateLimiter.register(SYNC_BLOCK_CHAIN.asByte(), 2); - p2pRateLimiter.register(FETCH_INV_DATA.asByte(), 1); + p2pRateLimiter.register(FETCH_INV_DATA.asByte(), 2); p2pRateLimiter.register(P2P_DISCONNECT.asByte(), 1); } diff --git a/framework/src/test/java/org/tron/core/net/P2pRateLimiterTest.java b/framework/src/test/java/org/tron/core/net/P2pRateLimiterTest.java index 8d6a10bb167..8a1d9c52749 100644 --- a/framework/src/test/java/org/tron/core/net/P2pRateLimiterTest.java +++ b/framework/src/test/java/org/tron/core/net/P2pRateLimiterTest.java @@ -12,8 +12,10 @@ public void test() { P2pRateLimiter limiter = new P2pRateLimiter(); limiter.register(SYNC_BLOCK_CHAIN.asByte(), 2); limiter.acquire(SYNC_BLOCK_CHAIN.asByte()); - limiter.tryAcquire(SYNC_BLOCK_CHAIN.asByte()); boolean ret = limiter.tryAcquire(SYNC_BLOCK_CHAIN.asByte()); + Assert.assertTrue(ret); + limiter.tryAcquire(SYNC_BLOCK_CHAIN.asByte()); + ret = limiter.tryAcquire(SYNC_BLOCK_CHAIN.asByte()); Assert.assertFalse(ret); ret = limiter.tryAcquire(FETCH_INV_DATA.asByte()); Assert.assertTrue(ret); diff --git a/framework/src/test/java/org/tron/core/net/messagehandler/FetchInvDataMsgHandlerTest.java b/framework/src/test/java/org/tron/core/net/messagehandler/FetchInvDataMsgHandlerTest.java index 82be8bc3d5d..f5c7abe3c4f 100644 --- a/framework/src/test/java/org/tron/core/net/messagehandler/FetchInvDataMsgHandlerTest.java +++ b/framework/src/test/java/org/tron/core/net/messagehandler/FetchInvDataMsgHandlerTest.java @@ -1,5 +1,7 @@ package org.tron.core.net.messagehandler; +import static org.tron.core.net.message.MessageTypes.FETCH_INV_DATA; + import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; import java.lang.reflect.Field; @@ -22,8 +24,6 @@ import org.tron.core.net.service.adv.AdvService; import org.tron.protos.Protocol; -import static org.tron.core.net.message.MessageTypes.FETCH_INV_DATA; - public class FetchInvDataMsgHandlerTest { @Test From 3b3ddac0b0a8d77043f4dc9e41488ea0d139d063 Mon Sep 17 00:00:00 2001 From: chengtx01 Date: Tue, 8 Jul 2025 17:21:57 +0800 Subject: [PATCH 12/59] fix(net):delete unused import --- .../src/main/java/org/tron/core/net/peer/PeerConnection.java | 1 - 1 file changed, 1 deletion(-) diff --git a/framework/src/main/java/org/tron/core/net/peer/PeerConnection.java b/framework/src/main/java/org/tron/core/net/peer/PeerConnection.java index b39f10494c4..70a428b2165 100644 --- a/framework/src/main/java/org/tron/core/net/peer/PeerConnection.java +++ b/framework/src/main/java/org/tron/core/net/peer/PeerConnection.java @@ -38,7 +38,6 @@ import org.tron.core.metrics.MetricsUtil; import org.tron.core.net.P2pRateLimiter; import org.tron.core.net.TronNetDelegate; -import org.tron.core.net.message.MessageTypes; import org.tron.core.net.message.adv.InventoryMessage; import org.tron.core.net.message.adv.TransactionsMessage; import org.tron.core.net.message.base.DisconnectMessage; From 5c8f032236aea50ff347df2b1928c8608a50c8fc Mon Sep 17 00:00:00 2001 From: bladehan1 Date: Tue, 8 Jul 2025 18:43:01 +0800 Subject: [PATCH 13/59] fix(CheckStyle): only fix CheckStyle --- .../test/java/org/tron/core/zksnark/ShieldedReceiveTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/framework/src/test/java/org/tron/core/zksnark/ShieldedReceiveTest.java b/framework/src/test/java/org/tron/core/zksnark/ShieldedReceiveTest.java index 2a7545f7a9b..d1738dfa39b 100755 --- a/framework/src/test/java/org/tron/core/zksnark/ShieldedReceiveTest.java +++ b/framework/src/test/java/org/tron/core/zksnark/ShieldedReceiveTest.java @@ -320,7 +320,8 @@ public void testBroadcastBeforeAllowZksnark() } /* - * generate spendproof, dataToBeSigned, outputproof example dynamically according to the params file + * generate spendproof, dataToBeSigned, + * outputproof example dynamically according to the params file */ public String[] generateSpendAndOutputParams() throws ZksnarkException, BadItemException { librustzcashInitZksnarkParams(); From 2454a928340002d289a53b5a51d9fbecf000fb94 Mon Sep 17 00:00:00 2001 From: chengtx01 Date: Tue, 8 Jul 2025 18:59:03 +0800 Subject: [PATCH 14/59] feat(net):optimize rate limit logic --- .../messagehandler/SyncBlockChainMsgHandler.java | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/framework/src/main/java/org/tron/core/net/messagehandler/SyncBlockChainMsgHandler.java b/framework/src/main/java/org/tron/core/net/messagehandler/SyncBlockChainMsgHandler.java index 4ab3c7dd172..71d268b22bc 100644 --- a/framework/src/main/java/org/tron/core/net/messagehandler/SyncBlockChainMsgHandler.java +++ b/framework/src/main/java/org/tron/core/net/messagehandler/SyncBlockChainMsgHandler.java @@ -31,13 +31,6 @@ public void processMessage(PeerConnection peer, TronMessage msg) throws P2pExcep SyncBlockChainMessage syncBlockChainMessage = (SyncBlockChainMessage) msg; - if (!peer.getP2pRateLimiter().tryAcquire(msg.getType().asByte())) { - // Discard messages that exceed the rate limit - logger.warn("{} message from peer {} exceeds the rate limit", - msg.getType(), peer.getInetSocketAddress()); - return; - } - if (!check(peer, syncBlockChainMessage)) { peer.disconnect(Protocol.ReasonCode.BAD_PROTOCOL); return; @@ -65,6 +58,14 @@ public void processMessage(PeerConnection peer, TronMessage msg) throws P2pExcep } private boolean check(PeerConnection peer, SyncBlockChainMessage msg) throws P2pException { + if (peer.getRemainNum() > 0 + && !peer.getP2pRateLimiter().tryAcquire(msg.getType().asByte())) { + // Discard messages that exceed the rate limit + logger.warn("{} message from peer {} exceeds the rate limit", + msg.getType(), peer.getInetSocketAddress()); + return false; + } + List blockIds = msg.getBlockIds(); if (CollectionUtils.isEmpty(blockIds)) { throw new P2pException(TypeEnum.BAD_MESSAGE, "SyncBlockChain blockIds is empty"); From 81c6528f382328bfe1408740f036773b4b09f7a0 Mon Sep 17 00:00:00 2001 From: chengtx01 Date: Fri, 11 Jul 2025 18:38:52 +0800 Subject: [PATCH 15/59] feat(net):Optimize rate limiting logic and add tests --- .../net/messagehandler/FetchInvDataMsgHandler.java | 14 +++++--------- .../org/tron/core/net/peer/PeerConnection.java | 10 +++++++--- .../messagehandler/FetchInvDataMsgHandlerTest.java | 7 +++++++ .../SyncBlockChainMsgHandlerTest.java | 5 +++++ 4 files changed, 24 insertions(+), 12 deletions(-) diff --git a/framework/src/main/java/org/tron/core/net/messagehandler/FetchInvDataMsgHandler.java b/framework/src/main/java/org/tron/core/net/messagehandler/FetchInvDataMsgHandler.java index 3b52b5798c8..30e200fc8e1 100644 --- a/framework/src/main/java/org/tron/core/net/messagehandler/FetchInvDataMsgHandler.java +++ b/framework/src/main/java/org/tron/core/net/messagehandler/FetchInvDataMsgHandler.java @@ -41,7 +41,6 @@ public class FetchInvDataMsgHandler implements TronMsgHandler { .maximumSize(1000).expireAfterWrite(1, TimeUnit.HOURS).build(); private static final int MAX_SIZE = 1_000_000; - private static final int MAX_FETCH_SIZE = 100; @Autowired private TronNetDelegate tronNetDelegate; @Autowired @@ -56,13 +55,6 @@ public void processMessage(PeerConnection peer, TronMessage msg) throws P2pExcep FetchInvDataMessage fetchInvDataMsg = (FetchInvDataMessage) msg; - if (peer.isNeedSyncFromUs() && !peer.getP2pRateLimiter().tryAcquire(msg.getType().asByte())) { - // Discard messages that exceed the rate limit - logger.warn("{} message from peer {} exceeds the rate limit", - msg.getType(), peer.getInetSocketAddress()); - return; - } - check(peer, fetchInvDataMsg); InventoryType type = fetchInvDataMsg.getInventoryType(); @@ -164,7 +156,11 @@ private void check(PeerConnection peer, FetchInvDataMessage fetchInvDataMsg) thr if (!peer.isNeedSyncFromUs()) { throw new P2pException(TypeEnum.BAD_MESSAGE, "no need sync"); } - if (fetchInvDataMsg.getHashList().size() > MAX_FETCH_SIZE) { + if (!peer.getP2pRateLimiter().tryAcquire(fetchInvDataMsg.getType().asByte())) { + throw new P2pException(TypeEnum.BAD_MESSAGE, fetchInvDataMsg.getType() + + " message exceeds the rate limit"); + } + if (fetchInvDataMsg.getHashList().size() > NetConstants.MAX_BLOCK_FETCH_PER_PEER) { throw new P2pException(TypeEnum.BAD_MESSAGE, "fetch too more blocks, size:" + fetchInvDataMsg.getHashList().size()); } diff --git a/framework/src/main/java/org/tron/core/net/peer/PeerConnection.java b/framework/src/main/java/org/tron/core/net/peer/PeerConnection.java index 70a428b2165..1d8cff3b060 100644 --- a/framework/src/main/java/org/tron/core/net/peer/PeerConnection.java +++ b/framework/src/main/java/org/tron/core/net/peer/PeerConnection.java @@ -59,6 +59,10 @@ public class PeerConnection { private static List relayNodes = Args.getInstance().getFastForwardNodes(); + private static final double SYNC_BLOCK_CHAIN_RATE = 3.0; + private static final double FETCH_INV_DATA_RATE = 3.0; + private static final double P2P_DISCONNECT_RATE = 1.0; + @Getter private PeerStatistics peerStatistics = new PeerStatistics(); @@ -171,9 +175,9 @@ public void setChannel(Channel channel) { } this.nodeStatistics = TronStatsManager.getNodeStatistics(channel.getInetAddress()); lastInteractiveTime = System.currentTimeMillis(); - p2pRateLimiter.register(SYNC_BLOCK_CHAIN.asByte(), 2); - p2pRateLimiter.register(FETCH_INV_DATA.asByte(), 2); - p2pRateLimiter.register(P2P_DISCONNECT.asByte(), 1); + p2pRateLimiter.register(SYNC_BLOCK_CHAIN.asByte(), SYNC_BLOCK_CHAIN_RATE); + p2pRateLimiter.register(FETCH_INV_DATA.asByte(), FETCH_INV_DATA_RATE); + p2pRateLimiter.register(P2P_DISCONNECT.asByte(), P2P_DISCONNECT_RATE); } public void setBlockBothHave(BlockId blockId) { diff --git a/framework/src/test/java/org/tron/core/net/messagehandler/FetchInvDataMsgHandlerTest.java b/framework/src/test/java/org/tron/core/net/messagehandler/FetchInvDataMsgHandlerTest.java index f5c7abe3c4f..acee2d3077d 100644 --- a/framework/src/test/java/org/tron/core/net/messagehandler/FetchInvDataMsgHandlerTest.java +++ b/framework/src/test/java/org/tron/core/net/messagehandler/FetchInvDataMsgHandlerTest.java @@ -58,6 +58,9 @@ public void testProcessMessage() throws Exception { Mockito.when(advService.getMessage(new Item(blockId, Protocol.Inventory.InventoryType.BLOCK))) .thenReturn(new BlockMessage(blockCapsule)); ReflectUtils.setFieldValue(fetchInvDataMsgHandler, "advService", advService); + P2pRateLimiter p2pRateLimiter = new P2pRateLimiter(); + p2pRateLimiter.register(FETCH_INV_DATA.asByte(), 2); + Mockito.when(peer.getP2pRateLimiter()).thenReturn(p2pRateLimiter); fetchInvDataMsgHandler.processMessage(peer, new FetchInvDataMessage(blockIds, Protocol.Inventory.InventoryType.BLOCK)); @@ -77,6 +80,9 @@ public void testSyncFetchCheck() { Cache advInvSpread = CacheBuilder.newBuilder().maximumSize(100) .expireAfterWrite(1, TimeUnit.HOURS).recordStats().build(); Mockito.when(peer.getAdvInvSpread()).thenReturn(advInvSpread); + P2pRateLimiter p2pRateLimiter = new P2pRateLimiter(); + p2pRateLimiter.register(FETCH_INV_DATA.asByte(), 2); + Mockito.when(peer.getP2pRateLimiter()).thenReturn(p2pRateLimiter); FetchInvDataMsgHandler fetchInvDataMsgHandler = new FetchInvDataMsgHandler(); @@ -113,6 +119,7 @@ public void testRateLimiter() { Mockito.when(peer.getAdvInvSpread()).thenReturn(advInvSpread); P2pRateLimiter p2pRateLimiter = new P2pRateLimiter(); p2pRateLimiter.register(FETCH_INV_DATA.asByte(), 1); + p2pRateLimiter.acquire(FETCH_INV_DATA.asByte()); Mockito.when(peer.getP2pRateLimiter()).thenReturn(p2pRateLimiter); FetchInvDataMsgHandler fetchInvDataMsgHandler = new FetchInvDataMsgHandler(); diff --git a/framework/src/test/java/org/tron/core/net/messagehandler/SyncBlockChainMsgHandlerTest.java b/framework/src/test/java/org/tron/core/net/messagehandler/SyncBlockChainMsgHandlerTest.java index e654e1c9cc2..eccab2aeb00 100644 --- a/framework/src/test/java/org/tron/core/net/messagehandler/SyncBlockChainMsgHandlerTest.java +++ b/framework/src/test/java/org/tron/core/net/messagehandler/SyncBlockChainMsgHandlerTest.java @@ -55,6 +55,7 @@ public void init() throws Exception { @Test public void testProcessMessage() throws Exception { try { + peer.setRemainNum(1); handler.processMessage(peer, new SyncBlockChainMessage(new ArrayList<>())); } catch (P2pException e) { Assert.assertEquals("SyncBlockChain blockIds is empty", e.getMessage()); @@ -71,6 +72,10 @@ public void testProcessMessage() throws Exception { Assert.assertNotNull(message.toString()); Assert.assertNotNull(((BlockInventoryMessage) message).getAnswerMessage()); Assert.assertFalse(f); + method.invoke(handler, peer, message); + method.invoke(handler, peer, message); + f = (boolean)method.invoke(handler, peer, message); + Assert.assertFalse(f); Method method1 = handler.getClass().getDeclaredMethod( "getLostBlockIds", List.class, BlockId.class); From 44dcce3f7ce3093cd0794d05d1ba2a215f379d21 Mon Sep 17 00:00:00 2001 From: halibobo1205 Date: Thu, 3 Jul 2025 11:19:49 +0800 Subject: [PATCH 16/59] feat(dependencies): update dependencies 1. Bump commons-io from 2.11.0 to 2.18.0 to fix CVE-2024-47554. 2. Bump jackson-databind from 2.13.4.2 to 2.18.3 to fix CVE-2023-35116, CVE-2025-52999. 3. Bump java-util from 1.8.0 to 3.2.0 to fix CVE-2023-34610. 4. Bump libp2p from 2.2.5 to 2.2.6. 5. Bump jetty from 9.4.53 to 9.4.57 to fix CVE-2024-8184. 6. Bump spring from 5.3.18 to 5.3.39 to fix CVE-2023-20863, CVE-2024-38820, CVE-2022-22968, CVE-2022-22970. 7. Remove spring-tx, spring-web, hamcrest-junit, guice, java-sizeof, vavr. --- build.gradle | 6 +- chainbase/build.gradle | 3 +- common/build.gradle | 6 +- .../org/tron/core/config/CommonConfig.java | 2 - framework/build.gradle | 13 +- .../http/InvalidMediaTypeException.java | 61 ++ .../org/springframework/http/MediaType.java | 841 ++++++++++++++++++ .../tron/common/utils/ObjectSizeUtilTest.java | 62 -- gradle/verification-metadata.xml | 409 ++++----- 9 files changed, 1110 insertions(+), 293 deletions(-) create mode 100644 framework/src/test/java/org/springframework/http/InvalidMediaTypeException.java create mode 100644 framework/src/test/java/org/springframework/http/MediaType.java delete mode 100644 framework/src/test/java/org/tron/common/utils/ObjectSizeUtilTest.java diff --git a/build.gradle b/build.gradle index 14b095b1795..e2d90c55db0 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,9 @@ allprojects { version = "1.0.0" apply plugin: "java-library" + ext { + springVersion = "5.3.39" + } } subprojects { @@ -41,8 +44,7 @@ subprojects { implementation group: 'org.slf4j', name: 'jcl-over-slf4j', version: '1.7.25' implementation group: 'ch.qos.logback', name: 'logback-classic', version: '1.2.13' implementation "com.google.code.findbugs:jsr305:3.0.0" - implementation group: 'org.springframework', name: 'spring-context', version: '5.3.18' - implementation group: 'org.springframework', name: 'spring-tx', version: '5.3.18' + implementation group: 'org.springframework', name: 'spring-context', version: "${springVersion}" implementation "org.apache.commons:commons-lang3:3.4" implementation group: 'org.apache.commons', name: 'commons-math', version: '2.2' implementation "org.apache.commons:commons-collections4:4.1" diff --git a/chainbase/build.gradle b/chainbase/build.gradle index bc82d9496c3..187ce9d32d3 100644 --- a/chainbase/build.gradle +++ b/chainbase/build.gradle @@ -10,7 +10,8 @@ dependencies { api project(":common") api project(":crypto") api "org.fusesource.jansi:jansi:$jansiVersion" - api 'io.github.tronprotocol:zksnark-java-sdk:1.0.0' + api group: 'commons-io', name: 'commons-io', version: '2.18.0' + api 'io.github.tronprotocol:zksnark-java-sdk:1.0.0' exclude(group: 'commons-io', module: 'commons-io') api 'org.reflections:reflections:0.9.11' } diff --git a/common/build.gradle b/common/build.gradle index c6ce8cf44f9..8a1c87c2e46 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -30,8 +30,8 @@ if (isWindows()) { } dependencies { - api group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.13.4.2' // https://github.com/FasterXML/jackson-databind/issues/3627 - api "com.cedarsoftware:java-util:1.8.0" + api group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.18.3' // https://github.com/FasterXML/jackson-databind/issues/3627 + api "com.cedarsoftware:java-util:3.2.0" api group: 'org.apache.httpcomponents', name: 'httpasyncclient', version: '4.1.1' api group: 'commons-codec', name: 'commons-codec', version: '1.11' api group: 'com.beust', name: 'jcommander', version: '1.78' @@ -44,7 +44,7 @@ dependencies { api 'org.aspectj:aspectjrt:1.8.13' api 'org.aspectj:aspectjweaver:1.8.13' api 'org.aspectj:aspectjtools:1.8.13' - api group: 'io.github.tronprotocol', name: 'libp2p', version: '2.2.5',{ + api group: 'io.github.tronprotocol', name: 'libp2p', version: '2.2.6',{ exclude group: 'io.grpc', module: 'grpc-context' exclude group: 'io.grpc', module: 'grpc-core' exclude group: 'io.grpc', module: 'grpc-netty' diff --git a/common/src/main/java/org/tron/core/config/CommonConfig.java b/common/src/main/java/org/tron/core/config/CommonConfig.java index 4d17a0faedd..b258a4cf3f5 100644 --- a/common/src/main/java/org/tron/core/config/CommonConfig.java +++ b/common/src/main/java/org/tron/core/config/CommonConfig.java @@ -21,10 +21,8 @@ import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy; -import org.springframework.transaction.annotation.EnableTransactionManagement; @Configuration -@EnableTransactionManagement @EnableAspectJAutoProxy @ComponentScan(basePackages = "org.tron") public class CommonConfig { diff --git a/framework/build.gradle b/framework/build.gradle index 0f04685f2d8..b4c20d2da76 100644 --- a/framework/build.gradle +++ b/framework/build.gradle @@ -38,15 +38,11 @@ dependencies { //local libraries implementation fileTree(dir: 'libs', include: '*.jar') // end local libraries - testImplementation group: 'org.hamcrest', name: 'hamcrest-junit', version: '1.0.0.1' - - implementation group: 'com.google.inject', name: 'guice', version: '4.1.0' implementation group: 'io.dropwizard.metrics', name: 'metrics-core', version: '3.1.2' implementation group: 'com.github.davidb', name: 'metrics-influxdb', version: '0.8.2' - implementation group: 'com.carrotsearch', name: 'java-sizeof', version: '0.0.5' // http - implementation 'org.eclipse.jetty:jetty-server:9.4.53.v20231009' - implementation 'org.eclipse.jetty:jetty-servlet:9.4.53.v20231009' + implementation 'org.eclipse.jetty:jetty-server:9.4.57.v20241219' + implementation 'org.eclipse.jetty:jetty-servlet:9.4.57.v20241219' implementation 'com.alibaba:fastjson:1.2.83' // end http @@ -56,14 +52,11 @@ dependencies { // https://mvnrepository.com/artifact/javax.portlet/portlet-api compileOnly group: 'javax.portlet', name: 'portlet-api', version: '3.0.1' - implementation "io.vavr:vavr:0.9.2" implementation (group: 'org.pf4j', name: 'pf4j', version: '3.10.0') { exclude group: "org.slf4j", module: "slf4j-api" } - testImplementation group: 'org.springframework', name: 'spring-test', version: '5.2.0.RELEASE' - testImplementation group: 'org.springframework', name: 'spring-web', version: '5.2.0.RELEASE' - + testImplementation group: 'org.springframework', name: 'spring-test', version: "${springVersion}" implementation group: 'org.zeromq', name: 'jeromq', version: '0.5.3' api project(":chainbase") api project(":protocol") diff --git a/framework/src/test/java/org/springframework/http/InvalidMediaTypeException.java b/framework/src/test/java/org/springframework/http/InvalidMediaTypeException.java new file mode 100644 index 00000000000..3b1e41f1756 --- /dev/null +++ b/framework/src/test/java/org/springframework/http/InvalidMediaTypeException.java @@ -0,0 +1,61 @@ +/* + * Copyright 2002-2018 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.http; + +import org.springframework.util.InvalidMimeTypeException; + +/** + * Exception thrown from {@link MediaType#parseMediaType(String)} in case of + * encountering an invalid media type specification String. + * + * @author Juergen Hoeller + * @since 3.2.2 + */ +@SuppressWarnings("serial") +public class InvalidMediaTypeException extends IllegalArgumentException { + + private final String mediaType; + + + /** + * Create a new InvalidMediaTypeException for the given media type. + * + * @param mediaType the offending media type + * @param message a detail message indicating the invalid part + */ + public InvalidMediaTypeException(String mediaType, String message) { + super("Invalid media type \"" + mediaType + "\": " + message); + this.mediaType = mediaType; + } + + /** + * Constructor that allows wrapping {@link InvalidMimeTypeException}. + */ + InvalidMediaTypeException(InvalidMimeTypeException ex) { + super(ex.getMessage(), ex); + this.mediaType = ex.getMimeType(); + } + + + /** + * Return the offending media type. + */ + public String getMediaType() { + return this.mediaType; + } + +} \ No newline at end of file diff --git a/framework/src/test/java/org/springframework/http/MediaType.java b/framework/src/test/java/org/springframework/http/MediaType.java new file mode 100644 index 00000000000..84962970235 --- /dev/null +++ b/framework/src/test/java/org/springframework/http/MediaType.java @@ -0,0 +1,841 @@ +/* + * Copyright 2002-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.http; + +import java.io.Serializable; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import org.springframework.lang.Nullable; +import org.springframework.util.Assert; +import org.springframework.util.CollectionUtils; +import org.springframework.util.InvalidMimeTypeException; +import org.springframework.util.MimeType; +import org.springframework.util.MimeTypeUtils; +import org.springframework.util.StringUtils; + +/** + * A subclass of {@link MimeType} that adds support for quality parameters + * as defined in the HTTP specification. + * + * @author Arjen Poutsma + * @author Juergen Hoeller + * @author Rossen Stoyanchev + * @author Sebastien Deleuze + * @author Kazuki Shimizu + * @author Sam Brannen + * @see + * HTTP 1.1: Semantics and Content, section 3.1.1.1 + * @since 3.0 + */ +public class MediaType extends MimeType implements Serializable { + + /** + * Public constant media type that includes all media ranges (i.e. "*/*"). + */ + public static final MediaType ALL; + /** + * A String equivalent of {@link MediaType#ALL}. + */ + public static final String ALL_VALUE = "*/*"; + /** + * Public constant media type for {@code application/atom+xml}. + */ + public static final MediaType APPLICATION_ATOM_XML; + /** + * A String equivalent of {@link MediaType#APPLICATION_ATOM_XML}. + */ + public static final String APPLICATION_ATOM_XML_VALUE = "application/atom+xml"; + /** + * Public constant media type for {@code application/cbor}. + * + * @since 5.2 + */ + public static final MediaType APPLICATION_CBOR; + /** + * A String equivalent of {@link MediaType#APPLICATION_CBOR}. + * + * @since 5.2 + */ + public static final String APPLICATION_CBOR_VALUE = "application/cbor"; + /** + * Public constant media type for {@code application/x-www-form-urlencoded}. + */ + public static final MediaType APPLICATION_FORM_URLENCODED; + /** + * A String equivalent of {@link MediaType#APPLICATION_FORM_URLENCODED}. + */ + public static final String APPLICATION_FORM_URLENCODED_VALUE = + "application/x-www-form-urlencoded"; + /** + * Public constant media type for {@code application/graphql+json}. + * + * @see GraphQL over HTTP spec + * @since 5.3.19 + */ + public static final MediaType APPLICATION_GRAPHQL; + /** + * A String equivalent of {@link MediaType#APPLICATION_GRAPHQL}. + * + * @since 5.3.19 + */ + public static final String APPLICATION_GRAPHQL_VALUE = "application/graphql+json"; + /** + * Public constant media type for {@code application/json}. + */ + public static final MediaType APPLICATION_JSON; + /** + * A String equivalent of {@link MediaType#APPLICATION_JSON}. + * + * @see #APPLICATION_JSON_UTF8_VALUE + */ + public static final String APPLICATION_JSON_VALUE = "application/json"; + /** + * Public constant media type for {@code application/json;charset=UTF-8}. + * + * @deprecated as of 5.2 in favor of {@link #APPLICATION_JSON} + * since major browsers like Chrome + * + * now comply with the specification and interpret correctly UTF-8 special + * characters without requiring a {@code charset=UTF-8} parameter. + */ + @Deprecated + public static final MediaType APPLICATION_JSON_UTF8; + /** + * A String equivalent of {@link MediaType#APPLICATION_JSON_UTF8}. + * + * @deprecated as of 5.2 in favor of {@link #APPLICATION_JSON_VALUE} + * since major browsers like Chrome + * + * now comply with the specification and interpret correctly UTF-8 special + * characters without requiring a {@code charset=UTF-8} parameter. + */ + @Deprecated + public static final String APPLICATION_JSON_UTF8_VALUE = "application/json;charset=UTF-8"; + /** + * Public constant media type for {@code application/octet-stream}. + */ + public static final MediaType APPLICATION_OCTET_STREAM; + /** + * A String equivalent of {@link MediaType#APPLICATION_OCTET_STREAM}. + */ + public static final String APPLICATION_OCTET_STREAM_VALUE = "application/octet-stream"; + /** + * Public constant media type for {@code application/pdf}. + * + * @since 4.3 + */ + public static final MediaType APPLICATION_PDF; + /** + * A String equivalent of {@link MediaType#APPLICATION_PDF}. + * + * @since 4.3 + */ + public static final String APPLICATION_PDF_VALUE = "application/pdf"; + /** + * Public constant media type for {@code application/problem+json}. + * + * @see + * Problem Details for HTTP APIs, 6.1. application/problem+json + * @since 5.0 + */ + public static final MediaType APPLICATION_PROBLEM_JSON; + /** + * A String equivalent of {@link MediaType#APPLICATION_PROBLEM_JSON}. + * + * @since 5.0 + */ + public static final String APPLICATION_PROBLEM_JSON_VALUE = "application/problem+json"; + /** + * Public constant media type for {@code application/problem+json}. + * + * @see + * Problem Details for HTTP APIs, 6.1. application/problem+json + * @since 5.0 + * @deprecated as of 5.2 in favor of {@link #APPLICATION_PROBLEM_JSON} + * since major browsers like Chrome + * + * now comply with the specification and interpret correctly UTF-8 special + * characters without requiring a {@code charset=UTF-8} parameter. + */ + @Deprecated + public static final MediaType APPLICATION_PROBLEM_JSON_UTF8; + /** + * A String equivalent of {@link MediaType#APPLICATION_PROBLEM_JSON_UTF8}. + * + * @since 5.0 + * @deprecated as of 5.2 in favor of {@link #APPLICATION_PROBLEM_JSON_VALUE} + * since major browsers like Chrome + * + * now comply with the specification and interpret correctly UTF-8 special + * characters without requiring a {@code charset=UTF-8} parameter. + */ + @Deprecated + public static final String APPLICATION_PROBLEM_JSON_UTF8_VALUE = + "application/problem+json;charset=UTF-8"; + /** + * Public constant media type for {@code application/problem+xml}. + * + * @see + * Problem Details for HTTP APIs, 6.2. application/problem+xml + * @since 5.0 + */ + public static final MediaType APPLICATION_PROBLEM_XML; + /** + * A String equivalent of {@link MediaType#APPLICATION_PROBLEM_XML}. + * + * @since 5.0 + */ + public static final String APPLICATION_PROBLEM_XML_VALUE = "application/problem+xml"; + /** + * Public constant media type for {@code application/rss+xml}. + * + * @since 4.3.6 + */ + public static final MediaType APPLICATION_RSS_XML; + /** + * A String equivalent of {@link MediaType#APPLICATION_RSS_XML}. + * + * @since 4.3.6 + */ + public static final String APPLICATION_RSS_XML_VALUE = "application/rss+xml"; + /** + * Public constant media type for {@code application/x-ndjson}. + * + * @since 5.3 + */ + public static final MediaType APPLICATION_NDJSON; + /** + * A String equivalent of {@link MediaType#APPLICATION_NDJSON}. + * + * @since 5.3 + */ + public static final String APPLICATION_NDJSON_VALUE = "application/x-ndjson"; + /** + * Public constant media type for {@code application/stream+json}. + * + * @since 5.0 + * @deprecated as of 5.3, see notice on {@link #APPLICATION_STREAM_JSON_VALUE}. + */ + @Deprecated + public static final MediaType APPLICATION_STREAM_JSON; + /** + * A String equivalent of {@link MediaType#APPLICATION_STREAM_JSON}. + * + * @since 5.0 + * @deprecated as of 5.3 since it originates from the W3C Activity Streams + * specification which has a more specific purpose and has been since + * replaced with a different mime type. Use {@link #APPLICATION_NDJSON} as + * a replacement or any other line-delimited JSON format (e.g. JSON Lines, + * JSON Text Sequences). + */ + @Deprecated + public static final String APPLICATION_STREAM_JSON_VALUE = "application/stream+json"; + /** + * Public constant media type for {@code application/xhtml+xml}. + */ + public static final MediaType APPLICATION_XHTML_XML; + /** + * A String equivalent of {@link MediaType#APPLICATION_XHTML_XML}. + */ + public static final String APPLICATION_XHTML_XML_VALUE = "application/xhtml+xml"; + /** + * Public constant media type for {@code application/xml}. + */ + public static final MediaType APPLICATION_XML; + /** + * A String equivalent of {@link MediaType#APPLICATION_XML}. + */ + public static final String APPLICATION_XML_VALUE = "application/xml"; + /** + * Public constant media type for {@code image/gif}. + */ + public static final MediaType IMAGE_GIF; + /** + * A String equivalent of {@link MediaType#IMAGE_GIF}. + */ + public static final String IMAGE_GIF_VALUE = "image/gif"; + /** + * Public constant media type for {@code image/jpeg}. + */ + public static final MediaType IMAGE_JPEG; + /** + * A String equivalent of {@link MediaType#IMAGE_JPEG}. + */ + public static final String IMAGE_JPEG_VALUE = "image/jpeg"; + /** + * Public constant media type for {@code image/png}. + */ + public static final MediaType IMAGE_PNG; + /** + * A String equivalent of {@link MediaType#IMAGE_PNG}. + */ + public static final String IMAGE_PNG_VALUE = "image/png"; + /** + * Public constant media type for {@code multipart/form-data}. + */ + public static final MediaType MULTIPART_FORM_DATA; + /** + * A String equivalent of {@link MediaType#MULTIPART_FORM_DATA}. + */ + public static final String MULTIPART_FORM_DATA_VALUE = "multipart/form-data"; + /** + * Public constant media type for {@code multipart/mixed}. + * + * @since 5.2 + */ + public static final MediaType MULTIPART_MIXED; + /** + * A String equivalent of {@link MediaType#MULTIPART_MIXED}. + * + * @since 5.2 + */ + public static final String MULTIPART_MIXED_VALUE = "multipart/mixed"; + /** + * Public constant media type for {@code multipart/related}. + * + * @since 5.2.5 + */ + public static final MediaType MULTIPART_RELATED; + /** + * A String equivalent of {@link MediaType#MULTIPART_RELATED}. + * + * @since 5.2.5 + */ + public static final String MULTIPART_RELATED_VALUE = "multipart/related"; + /** + * Public constant media type for {@code text/event-stream}. + * + * @see Server-Sent Events W3C recommendation + * @since 4.3.6 + */ + public static final MediaType TEXT_EVENT_STREAM; + /** + * A String equivalent of {@link MediaType#TEXT_EVENT_STREAM}. + * + * @since 4.3.6 + */ + public static final String TEXT_EVENT_STREAM_VALUE = "text/event-stream"; + /** + * Public constant media type for {@code text/html}. + */ + public static final MediaType TEXT_HTML; + /** + * A String equivalent of {@link MediaType#TEXT_HTML}. + */ + public static final String TEXT_HTML_VALUE = "text/html"; + /** + * Public constant media type for {@code text/markdown}. + * + * @since 4.3 + */ + public static final MediaType TEXT_MARKDOWN; + /** + * A String equivalent of {@link MediaType#TEXT_MARKDOWN}. + * + * @since 4.3 + */ + public static final String TEXT_MARKDOWN_VALUE = "text/markdown"; + /** + * Public constant media type for {@code text/plain}. + */ + public static final MediaType TEXT_PLAIN; + /** + * A String equivalent of {@link MediaType#TEXT_PLAIN}. + */ + public static final String TEXT_PLAIN_VALUE = "text/plain"; + /** + * Public constant media type for {@code text/xml}. + */ + public static final MediaType TEXT_XML; + /** + * A String equivalent of {@link MediaType#TEXT_XML}. + */ + public static final String TEXT_XML_VALUE = "text/xml"; + private static final long serialVersionUID = 2069937152339670231L; + private static final String PARAM_QUALITY_FACTOR = "q"; + /** + * Comparator used by {@link #sortByQualityValue(List)}. + */ + public static final Comparator QUALITY_VALUE_COMPARATOR = (mediaType1, mediaType2) -> { + double quality1 = mediaType1.getQualityValue(); + double quality2 = mediaType2.getQualityValue(); + int qualityComparison = Double.compare(quality2, quality1); + if (qualityComparison != 0) { + return qualityComparison; // audio/*;q=0.7 < audio/*;q=0.3 + } else if (mediaType1.isWildcardType() && !mediaType2.isWildcardType()) { // */* < audio/* + return 1; + } else if (mediaType2.isWildcardType() && !mediaType1.isWildcardType()) { // audio/* > */* + return -1; + } else if (!mediaType1.getType().equals(mediaType2.getType())) { // audio/basic == text/html + return 0; + } else { // mediaType1.getType().equals(mediaType2.getType()) + if (mediaType1.isWildcardSubtype() && !mediaType2.isWildcardSubtype()) { + // audio/* < audio/basic + return 1; + } else if (mediaType2.isWildcardSubtype() && !mediaType1.isWildcardSubtype()) { + // audio/basic > audio/* + return -1; + } else if (!mediaType1.getSubtype().equals(mediaType2.getSubtype())) { + // audio/basic == audio/wave + return 0; + } else { + int paramsSize1 = mediaType1.getParameters().size(); + int paramsSize2 = mediaType2.getParameters().size(); + return Integer.compare(paramsSize2, paramsSize1); // audio/basic;level=1 < audio/basic + } + } + }; + /** + * Comparator used by {@link #sortBySpecificity(List)}. + */ + public static final Comparator SPECIFICITY_COMPARATOR = + new SpecificityComparator() { + + @Override + protected int compareParameters(MediaType mediaType1, MediaType mediaType2) { + double quality1 = mediaType1.getQualityValue(); + double quality2 = mediaType2.getQualityValue(); + int qualityComparison = Double.compare(quality2, quality1); + if (qualityComparison != 0) { + return qualityComparison; // audio/*;q=0.7 < audio/*;q=0.3 + } + return super.compareParameters(mediaType1, mediaType2); + } + }; + + static { + // Not using "valueOf' to avoid static init cost + ALL = new MediaType("*", "*"); + APPLICATION_ATOM_XML = new MediaType("application", "atom+xml"); + APPLICATION_CBOR = new MediaType("application", "cbor"); + APPLICATION_FORM_URLENCODED = new MediaType("application", "x-www-form-urlencoded"); + APPLICATION_GRAPHQL = new MediaType("application", "graphql+json"); + APPLICATION_JSON = new MediaType("application", "json"); + APPLICATION_JSON_UTF8 = new MediaType("application", "json", StandardCharsets.UTF_8); + APPLICATION_NDJSON = new MediaType("application", "x-ndjson"); + APPLICATION_OCTET_STREAM = new MediaType("application", "octet-stream"); + APPLICATION_PDF = new MediaType("application", "pdf"); + APPLICATION_PROBLEM_JSON = new MediaType("application", "problem+json"); + APPLICATION_PROBLEM_JSON_UTF8 = new MediaType("application", "problem+json", + StandardCharsets.UTF_8); + APPLICATION_PROBLEM_XML = new MediaType("application", "problem+xml"); + APPLICATION_RSS_XML = new MediaType("application", "rss+xml"); + APPLICATION_STREAM_JSON = new MediaType("application", "stream+json"); + APPLICATION_XHTML_XML = new MediaType("application", "xhtml+xml"); + APPLICATION_XML = new MediaType("application", "xml"); + IMAGE_GIF = new MediaType("image", "gif"); + IMAGE_JPEG = new MediaType("image", "jpeg"); + IMAGE_PNG = new MediaType("image", "png"); + MULTIPART_FORM_DATA = new MediaType("multipart", "form-data"); + MULTIPART_MIXED = new MediaType("multipart", "mixed"); + MULTIPART_RELATED = new MediaType("multipart", "related"); + TEXT_EVENT_STREAM = new MediaType("text", "event-stream"); + TEXT_HTML = new MediaType("text", "html"); + TEXT_MARKDOWN = new MediaType("text", "markdown"); + TEXT_PLAIN = new MediaType("text", "plain"); + TEXT_XML = new MediaType("text", "xml"); + } + + /** + * Create a new {@code MediaType} for the given primary type. + *

The {@linkplain #getSubtype() subtype} is set to "*", parameters empty. + * + * @param type the primary type + * @throws IllegalArgumentException if any of the parameters contain illegal characters + */ + public MediaType(String type) { + super(type); + } + + /** + * Create a new {@code MediaType} for the given primary type and subtype. + *

The parameters are empty. + * + * @param type the primary type + * @param subtype the subtype + * @throws IllegalArgumentException if any of the parameters contain illegal characters + */ + public MediaType(String type, String subtype) { + super(type, subtype, Collections.emptyMap()); + } + + /** + * Create a new {@code MediaType} for the given type, subtype, and character set. + * + * @param type the primary type + * @param subtype the subtype + * @param charset the character set + * @throws IllegalArgumentException if any of the parameters contain illegal characters + */ + public MediaType(String type, String subtype, Charset charset) { + super(type, subtype, charset); + } + + /** + * Create a new {@code MediaType} for the given type, subtype, and quality value. + * + * @param type the primary type + * @param subtype the subtype + * @param qualityValue the quality value + * @throws IllegalArgumentException if any of the parameters contain illegal characters + */ + public MediaType(String type, String subtype, double qualityValue) { + this(type, subtype, Collections.singletonMap(PARAM_QUALITY_FACTOR, + Double.toString(qualityValue))); + } + + /** + * Copy-constructor that copies the type, subtype and parameters of the given + * {@code MediaType}, and allows to set the specified character set. + * + * @param other the other media type + * @param charset the character set + * @throws IllegalArgumentException if any of the parameters contain illegal characters + * @since 4.3 + */ + public MediaType(MediaType other, Charset charset) { + super(other, charset); + } + + /** + * Copy-constructor that copies the type and subtype of the given {@code MediaType}, + * and allows for different parameters. + * + * @param other the other media type + * @param parameters the parameters, may be {@code null} + * @throws IllegalArgumentException if any of the parameters contain illegal characters + */ + public MediaType(MediaType other, @Nullable Map parameters) { + super(other.getType(), other.getSubtype(), parameters); + } + + + /** + * Create a new {@code MediaType} for the given type, subtype, and parameters. + * + * @param type the primary type + * @param subtype the subtype + * @param parameters the parameters, may be {@code null} + * @throws IllegalArgumentException if any of the parameters contain illegal characters + */ + public MediaType(String type, String subtype, @Nullable Map parameters) { + super(type, subtype, parameters); + } + + /** + * Create a new {@code MediaType} for the given {@link MimeType}. + * The type, subtype and parameters information is copied and {@code MediaType}-specific + * checks on parameters are performed. + * + * @param mimeType the MIME type + * @throws IllegalArgumentException if any of the parameters contain illegal characters + * @since 5.3 + */ + public MediaType(MimeType mimeType) { + super(mimeType); + getParameters().forEach(this::checkParameters); + } + + /** + * Parse the given String value into a {@code MediaType} object, + * with this method name following the 'valueOf' naming convention + * (as supported by {@link org.springframework.core.convert.ConversionService}. + * + * @param value the string to parse + * @throws InvalidMediaTypeException if the media type value cannot be parsed + * @see #parseMediaType(String) + */ + public static MediaType valueOf(String value) { + return parseMediaType(value); + } + + /** + * Parse the given String into a single {@code MediaType}. + * + * @param mediaType the string to parse + * @return the media type + * @throws InvalidMediaTypeException if the media type value cannot be parsed + */ + public static MediaType parseMediaType(String mediaType) { + MimeType type; + try { + type = MimeTypeUtils.parseMimeType(mediaType); + } catch (InvalidMimeTypeException ex) { + throw new InvalidMediaTypeException(ex); + } + try { + return new MediaType(type); + } catch (IllegalArgumentException ex) { + throw new InvalidMediaTypeException(mediaType, ex.getMessage()); + } + } + + /** + * Parse the comma-separated string into a list of {@code MediaType} objects. + *

This method can be used to parse an Accept or Content-Type header. + * + * @param mediaTypes the string to parse + * @return the list of media types + * @throws InvalidMediaTypeException if the media type value cannot be parsed + */ + public static List parseMediaTypes(@Nullable String mediaTypes) { + if (!StringUtils.hasLength(mediaTypes)) { + return Collections.emptyList(); + } + // Avoid using java.util.stream.Stream in hot paths + List tokenizedTypes = MimeTypeUtils.tokenize(mediaTypes); + List result = new ArrayList<>(tokenizedTypes.size()); + for (String type : tokenizedTypes) { + if (StringUtils.hasText(type)) { + result.add(parseMediaType(type)); + } + } + return result; + } + + /** + * Parse the given list of (potentially) comma-separated strings into a + * list of {@code MediaType} objects. + *

This method can be used to parse an Accept or Content-Type header. + * + * @param mediaTypes the string to parse + * @return the list of media types + * @throws InvalidMediaTypeException if the media type value cannot be parsed + * @since 4.3.2 + */ + public static List parseMediaTypes(@Nullable List mediaTypes) { + if (CollectionUtils.isEmpty(mediaTypes)) { + return Collections.emptyList(); + } else if (mediaTypes.size() == 1) { + return parseMediaTypes(mediaTypes.get(0)); + } else { + List result = new ArrayList<>(8); + for (String mediaType : mediaTypes) { + result.addAll(parseMediaTypes(mediaType)); + } + return result; + } + } + + /** + * Re-create the given mime types as media types. + * + * @since 5.0 + */ + public static List asMediaTypes(List mimeTypes) { + List mediaTypes = new ArrayList<>(mimeTypes.size()); + for (MimeType mimeType : mimeTypes) { + mediaTypes.add(MediaType.asMediaType(mimeType)); + } + return mediaTypes; + } + + /** + * Re-create the given mime type as a media type. + * + * @since 5.0 + */ + public static MediaType asMediaType(MimeType mimeType) { + if (mimeType instanceof MediaType) { + return (MediaType) mimeType; + } + return new MediaType(mimeType.getType(), mimeType.getSubtype(), mimeType.getParameters()); + } + + /** + * Return a string representation of the given list of {@code MediaType} objects. + *

This method can be used to for an {@code Accept} or {@code Content-Type} header. + * + * @param mediaTypes the media types to create a string representation for + * @return the string representation + */ + public static String toString(Collection mediaTypes) { + return MimeTypeUtils.toString(mediaTypes); + } + + /** + * Sorts the given list of {@code MediaType} objects by specificity. + *

Given two media types: + *

    + *
  1. if either media type has a {@linkplain #isWildcardType() wildcard type}, + * then the media type without the wildcard is ordered before the other.
  2. + *
  3. if the two media types have different {@linkplain #getType() types}, + * then they are considered equal and remain their current order.
  4. + *
  5. if either media type has a {@linkplain #isWildcardSubtype() wildcard subtype}, + * then the media type without the wildcard is sorted before the other.
  6. + *
  7. if the two media types have different {@linkplain #getSubtype() subtypes}, + * then they are considered equal and remain their current order.
  8. + *
  9. if the two media types have different {@linkplain #getQualityValue() quality value}, + * then the media type with the highest quality value is ordered before the other.
  10. + *
  11. if the two media types have a different amount of + * {@linkplain #getParameter(String) parameters}, then the + * media type with the most parameters is ordered before the other.
  12. + *
+ *

For example: + *

audio/basic < audio/* < */*
+ *
audio/* < audio/*;q=0.7; audio/*;q=0.3
+ *
audio/basic;level=1 < audio/basic
+ *
audio/basic == text/html
+ *
audio/basic == audio/wave
+ * + * @param mediaTypes the list of media types to be sorted + * @see HTTP 1.1: Semantics + * and Content, section 5.3.2 + */ + public static void sortBySpecificity(List mediaTypes) { + Assert.notNull(mediaTypes, "'mediaTypes' must not be null"); + if (mediaTypes.size() > 1) { + mediaTypes.sort(SPECIFICITY_COMPARATOR); + } + } + + /** + * Sorts the given list of {@code MediaType} objects by quality value. + *

Given two media types: + *

    + *
  1. if the two media types have different {@linkplain #getQualityValue() quality value}, + * then the media type with the highest quality value is ordered before the other.
  2. + *
  3. if either media type has a {@linkplain #isWildcardType() wildcard type}, + * then the media type without the wildcard is ordered before the other.
  4. + *
  5. if the two media types have different {@linkplain #getType() types}, + * then they are considered equal and remain their current order.
  6. + *
  7. if either media type has a {@linkplain #isWildcardSubtype() wildcard subtype}, + * then the media type without the wildcard is sorted before the other.
  8. + *
  9. if the two media types have different {@linkplain #getSubtype() subtypes}, + * then they are considered equal and remain their current order.
  10. + *
  11. if the two media types have a different amount of + * {@linkplain #getParameter(String) parameters}, then the + * media type with the most parameters is ordered before the other.
  12. + *
+ * + * @param mediaTypes the list of media types to be sorted + * @see #getQualityValue() + */ + public static void sortByQualityValue(List mediaTypes) { + Assert.notNull(mediaTypes, "'mediaTypes' must not be null"); + if (mediaTypes.size() > 1) { + mediaTypes.sort(QUALITY_VALUE_COMPARATOR); + } + } + + /** + * Sorts the given list of {@code MediaType} objects by specificity as the + * primary criteria and quality value the secondary. + * + * @see MediaType#sortBySpecificity(List) + * @see MediaType#sortByQualityValue(List) + */ + public static void sortBySpecificityAndQuality(List mediaTypes) { + Assert.notNull(mediaTypes, "'mediaTypes' must not be null"); + if (mediaTypes.size() > 1) { + mediaTypes.sort( + MediaType.SPECIFICITY_COMPARATOR.thenComparing(MediaType.QUALITY_VALUE_COMPARATOR)); + } + } + + @Override + protected void checkParameters(String parameter, String value) { + super.checkParameters(parameter, value); + if (PARAM_QUALITY_FACTOR.equals(parameter)) { + String unquotedValue = unquote(value); + double d = Double.parseDouble(unquotedValue); + Assert.isTrue(d >= 0D && d <= 1D, + () -> "Invalid quality value \"" + unquotedValue + "\": should be between 0.0 and 1.0"); + } + } + + /** + * Return the quality factor, as indicated by a {@code q} parameter, if any. + * Defaults to {@code 1.0}. + * + * @return the quality factor as double value + */ + public double getQualityValue() { + String qualityFactor = getParameter(PARAM_QUALITY_FACTOR); + return (qualityFactor != null ? Double.parseDouble(unquote(qualityFactor)) : 1D); + } + + /** + * Indicate whether this {@code MediaType} includes the given media type. + *

For instance, {@code text/*} includes {@code text/plain} and {@code text/html}, + * and {@code application/*+xml} includes {@code application/soap+xml}, etc. + * This method is not symmetric. + *

Simply calls {@link MimeType#includes(MimeType)} but declared with a + * {@code MediaType} parameter for binary backwards compatibility. + * + * @param other the reference media type with which to compare + * @return {@code true} if this media type includes the given media type; + * {@code false} otherwise + */ + public boolean includes(@Nullable MediaType other) { + return super.includes(other); + } + + /** + * Indicate whether this {@code MediaType} is compatible with the given media type. + *

For instance, {@code text/*} is compatible with {@code text/plain}, + * {@code text/html}, and vice versa. In effect, this method is similar to + * {@link #includes}, except that it is symmetric. + *

Simply calls {@link MimeType#isCompatibleWith(MimeType)} but declared with a + * {@code MediaType} parameter for binary backwards compatibility. + * + * @param other the reference media type with which to compare + * @return {@code true} if this media type is compatible with the given media type; + * {@code false} otherwise + */ + public boolean isCompatibleWith(@Nullable MediaType other) { + return super.isCompatibleWith(other); + } + + /** + * Return a replica of this instance with the quality value of the given {@code MediaType}. + * + * @return the same instance if the given MediaType doesn't have a quality value, + * or a new one otherwise + */ + public MediaType copyQualityValue(MediaType mediaType) { + if (!mediaType.getParameters().containsKey(PARAM_QUALITY_FACTOR)) { + return this; + } + Map params = new LinkedHashMap<>(getParameters()); + params.put(PARAM_QUALITY_FACTOR, mediaType.getParameters().get(PARAM_QUALITY_FACTOR)); + return new MediaType(this, params); + } + + /** + * Return a replica of this instance with its quality value removed. + * + * @return the same instance if the media type doesn't contain a quality value, + * or a new one otherwise + */ + public MediaType removeQualityValue() { + if (!getParameters().containsKey(PARAM_QUALITY_FACTOR)) { + return this; + } + Map params = new LinkedHashMap<>(getParameters()); + params.remove(PARAM_QUALITY_FACTOR); + return new MediaType(this, params); + } + +} \ No newline at end of file diff --git a/framework/src/test/java/org/tron/common/utils/ObjectSizeUtilTest.java b/framework/src/test/java/org/tron/common/utils/ObjectSizeUtilTest.java deleted file mode 100644 index c4c72991979..00000000000 --- a/framework/src/test/java/org/tron/common/utils/ObjectSizeUtilTest.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * java-tron is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * java-tron is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package org.tron.common.utils; - -import static org.junit.Assert.assertEquals; - -import org.junit.Test; - -public class ObjectSizeUtilTest { - - @Test - public void testGetObjectSize() { - - Person person = new Person(); - assertEquals(48, com.carrotsearch.sizeof.RamUsageEstimator.sizeOf(person)); - Person person1 = new Person(1, "tom", new int[]{}); - assertEquals(112, com.carrotsearch.sizeof.RamUsageEstimator.sizeOf(person1)); - - Person person2 = new Person(1, "tom", new int[]{100}); - assertEquals(120, com.carrotsearch.sizeof.RamUsageEstimator.sizeOf(person2)); - - Person person3 = new Person(1, "tom", new int[]{100, 100}); - assertEquals(120, com.carrotsearch.sizeof.RamUsageEstimator.sizeOf(person3)); - Person person4 = new Person(1, "tom", new int[]{100, 100, 100}); - assertEquals(128, com.carrotsearch.sizeof.RamUsageEstimator.sizeOf(person4)); - Person person5 = new Person(1, "tom", new int[]{100, 100, 100, 100}); - assertEquals(128, com.carrotsearch.sizeof.RamUsageEstimator.sizeOf(person5)); - Person person6 = new Person(1, "tom", new int[]{100, 100, 100, 100, 100}); - assertEquals(136, com.carrotsearch.sizeof.RamUsageEstimator.sizeOf(person6)); - - } - - class Person { - - int age; - String name; - int[] scores; - - public Person() { - } - - public Person(int age, String name, int[] scores) { - this.age = age; - this.name = name; - this.scores = scores; - } - } - -} diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index b3c879f6b40..5b80b8d706e 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -150,28 +150,12 @@ - - - + + + - - - - - - - - - - - - - - - - - - + + @@ -179,9 +163,9 @@ - - - + + + @@ -189,9 +173,9 @@ - - - + + + @@ -199,9 +183,9 @@ - - - + + + @@ -209,9 +193,9 @@ - - - + + + @@ -219,15 +203,15 @@ - - - + + + - - + + - - + + @@ -235,15 +219,15 @@ - - - + + + - - + + - - + + @@ -251,15 +235,15 @@ - - - + + + - - + + - - + + @@ -269,6 +253,9 @@ + + + @@ -483,19 +470,6 @@ - - - - - - - - - - - - - @@ -728,6 +702,14 @@ + + + + + + + + @@ -820,12 +802,15 @@ - - - + + + + + + - - + + @@ -1019,6 +1004,9 @@ + + + @@ -1086,27 +1074,6 @@ - - - - - - - - - - - - - - - - - - - - - @@ -1298,9 +1265,9 @@ - - - + + + @@ -1393,16 +1360,16 @@ - - - - - + + + + + @@ -1597,6 +1564,9 @@ + + + @@ -1666,65 +1636,65 @@ - - - + + + - - + + - - - + + + - - + + - - - + + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + @@ -1807,14 +1777,6 @@ - - - - - - - - @@ -1823,27 +1785,19 @@ - - - - - - - - - - - + + + - - - + + + @@ -1943,6 +1897,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + @@ -2207,81 +2185,86 @@ - - - + + + - - + + - - - - - - - + + - - - + + + - - + + - - - - - - - + + - - - + + + + + + - - + + - - - + + + + + + - - + + - - - + + + - - + + + + + - - - + + + + + + - - + + - - - + + + + + + - - + + - - - + + + From e9403b08c3f0e4ba625c93753c6530fb3a65e3f1 Mon Sep 17 00:00:00 2001 From: liuxincheng Date: Mon, 7 Jul 2025 15:40:28 +0800 Subject: [PATCH 17/59] feat(approving): convert the approving window configuration to chain governance --- .../core/actuator/ProposalCreateActuator.java | 5 +++-- .../org/tron/core/utils/ProposalUtil.java | 19 +++++++++++++++++- .../core/store/DynamicPropertiesStore.java | 12 +++++++++++ .../common/parameter/CommonParameter.java | 8 ++++++++ .../src/main/java/org/tron/core/Constant.java | 7 +++++++ .../java/org/tron/core/config/Parameter.java | 5 +++-- .../src/main/java/org/tron/core/Wallet.java | 5 +++++ .../java/org/tron/core/config/args/Args.java | 6 ++++++ .../tron/core/consensus/ProposalService.java | 4 ++++ .../java/org/tron/common/ParameterTest.java | 2 ++ .../core/services/ProposalServiceTest.java | 20 +++++++++++++++++++ 11 files changed, 88 insertions(+), 5 deletions(-) diff --git a/actuator/src/main/java/org/tron/core/actuator/ProposalCreateActuator.java b/actuator/src/main/java/org/tron/core/actuator/ProposalCreateActuator.java index a3639cca07f..41c501bfff0 100755 --- a/actuator/src/main/java/org/tron/core/actuator/ProposalCreateActuator.java +++ b/actuator/src/main/java/org/tron/core/actuator/ProposalCreateActuator.java @@ -3,13 +3,13 @@ import static org.tron.core.actuator.ActuatorConstant.ACCOUNT_EXCEPTION_STR; import static org.tron.core.actuator.ActuatorConstant.NOT_EXIST_STR; import static org.tron.core.actuator.ActuatorConstant.WITNESS_EXCEPTION_STR; +import static org.tron.core.config.Parameter.ChainConstant.BLOCK_PRODUCED_INTERVAL; import com.google.protobuf.ByteString; import com.google.protobuf.InvalidProtocolBufferException; import java.util.Map; import java.util.Objects; import lombok.extern.slf4j.Slf4j; -import org.tron.common.parameter.CommonParameter; import org.tron.common.utils.DecodeUtil; import org.tron.common.utils.StringUtil; import org.tron.core.capsule.ProposalCapsule; @@ -53,7 +53,8 @@ public boolean execute(Object result) throws ContractExeException { long currentMaintenanceTime = chainBaseManager.getDynamicPropertiesStore().getNextMaintenanceTime(); - long now3 = now + CommonParameter.getInstance().getProposalExpireTime(); + long now3 = now + chainBaseManager.getDynamicPropertiesStore().getProposalVotingWindow() + * BLOCK_PRODUCED_INTERVAL; long round = (now3 - currentMaintenanceTime) / maintenanceTimeInterval; long expirationTime = currentMaintenanceTime + (round + 1) * maintenanceTimeInterval; diff --git a/actuator/src/main/java/org/tron/core/utils/ProposalUtil.java b/actuator/src/main/java/org/tron/core/utils/ProposalUtil.java index 0bad940da7e..053146bb9ac 100644 --- a/actuator/src/main/java/org/tron/core/utils/ProposalUtil.java +++ b/actuator/src/main/java/org/tron/core/utils/ProposalUtil.java @@ -4,6 +4,8 @@ import static org.tron.core.Constant.CREATE_ACCOUNT_TRANSACTION_MIN_BYTE_SIZE; import static org.tron.core.Constant.DYNAMIC_ENERGY_INCREASE_FACTOR_RANGE; import static org.tron.core.Constant.DYNAMIC_ENERGY_MAX_FACTOR_RANGE; +import static org.tron.core.Constant.MAX_PROPOSAL_VOTING_WINDOW; +import static org.tron.core.Constant.MIN_PROPOSAL_VOTING_WINDOW; import static org.tron.core.config.Parameter.ChainConstant.ONE_YEAR_BLOCK_NUMBERS; import org.tron.common.utils.ForkController; @@ -839,6 +841,20 @@ public static void validator(DynamicPropertiesStore dynamicPropertiesStore, } break; } + case PROPOSAL_VOTING_WINDOW: { + if (!forkController.pass(ForkBlockVersionEnum.VERSION_4_8_1)) { + throw new ContractValidateException( + "Bad chain parameter id [PROPOSAL_VOTING_WINDOW]"); + } + if (value <= MIN_PROPOSAL_VOTING_WINDOW + || value > MAX_PROPOSAL_VOTING_WINDOW) { + throw new ContractValidateException( + "This value[PROPOSAL_VOTING_WINDOW] is only allowed to be greater than " + + MIN_PROPOSAL_VOTING_WINDOW + " and less than or equal to " + + MAX_PROPOSAL_VOTING_WINDOW + "!"); + } + break; + } default: break; } @@ -921,7 +937,8 @@ public enum ProposalType { // current value, value range ALLOW_TVM_CANCUN(83), // 0, 1 ALLOW_STRICT_MATH(87), // 0, 1 CONSENSUS_LOGIC_OPTIMIZATION(88), // 0, 1 - ALLOW_TVM_BLOB(89); // 0, 1 + ALLOW_TVM_BLOB(89), // 0, 1 + PROPOSAL_VOTING_WINDOW(92); // (0, 10512000] private long code; diff --git a/chainbase/src/main/java/org/tron/core/store/DynamicPropertiesStore.java b/chainbase/src/main/java/org/tron/core/store/DynamicPropertiesStore.java index 91b0cff68a0..81ae65dd880 100644 --- a/chainbase/src/main/java/org/tron/core/store/DynamicPropertiesStore.java +++ b/chainbase/src/main/java/org/tron/core/store/DynamicPropertiesStore.java @@ -231,6 +231,7 @@ public class DynamicPropertiesStore extends TronStoreWithRevoking private static final byte[] ALLOW_TVM_CANCUN = "ALLOW_TVM_CANCUN".getBytes(); private static final byte[] ALLOW_TVM_BLOB = "ALLOW_TVM_BLOB".getBytes(); + private static final byte[] PROPOSAL_VOTING_WINDOW = "PROPOSAL_VOTING_WINDOW".getBytes(); @Autowired private DynamicPropertiesStore(@Value("properties") String dbName) { @@ -2946,6 +2947,17 @@ public long getAllowTvmBlob() { .orElse(CommonParameter.getInstance().getAllowTvmBlob()); } + public void saveProposalVotingWindow(long proposalVotingWindow) { + this.put(PROPOSAL_VOTING_WINDOW, new BytesCapsule(ByteArray.fromLong(proposalVotingWindow))); + } + + public long getProposalVotingWindow() { + return Optional.ofNullable(getUnchecked(PROPOSAL_VOTING_WINDOW)) + .map(BytesCapsule::getData) + .map(ByteArray::toLong) + .orElse(CommonParameter.getInstance().getProposalVotingWindow()); + } + private static class DynamicResourceProperties { private static final byte[] ONE_DAY_NET_LIMIT = "ONE_DAY_NET_LIMIT".getBytes(); diff --git a/common/src/main/java/org/tron/common/parameter/CommonParameter.java b/common/src/main/java/org/tron/common/parameter/CommonParameter.java index 45893970fb0..a06e31a9262 100644 --- a/common/src/main/java/org/tron/common/parameter/CommonParameter.java +++ b/common/src/main/java/org/tron/common/parameter/CommonParameter.java @@ -724,6 +724,14 @@ public class CommonParameter { @Setter public long allowTvmBlob; + /** + * Committee parameter. + * Indicate the number of blocks after which the proposal approval window will take effect. + */ + @Getter + @Setter + public long proposalVotingWindow; + private static double calcMaxTimeRatio() { //return max(2.0, min(5.0, 5 * 4.0 / max(Runtime.getRuntime().availableProcessors(), 1))); return 5.0; diff --git a/common/src/main/java/org/tron/core/Constant.java b/common/src/main/java/org/tron/core/Constant.java index c5a8a02fb4e..612de614d28 100644 --- a/common/src/main/java/org/tron/core/Constant.java +++ b/common/src/main/java/org/tron/core/Constant.java @@ -39,6 +39,12 @@ public class Constant { public static final long MAX_CONTRACT_RESULT_SIZE = 2L; public static final long PB_DEFAULT_ENERGY_LIMIT = 0L; public static final long CREATOR_DEFAULT_ENERGY_LIMIT = 1000 * 10_000L; + public static final long MIN_PROPOSAL_VOTING_WINDOW = 0L; // Number of blocks to 0 day + public static final long MAX_PROPOSAL_VOTING_WINDOW = 10512000L; // Number of blocks to 365 days + /** + * default number of blocks to 3 days + */ + public static final long DEFAULT_PROPOSAL_VOTING_WINDOW = 86400L; // Numbers @@ -405,4 +411,5 @@ public class Constant { public static final String COMMITTEE_ALLOW_TVM_CANCUN = "committee.allowTvmCancun"; public static final String COMMITTEE_ALLOW_TVM_BLOB = "committee.allowTvmBlob"; + public static final String COMMITTEE_PROPOSAL_VOTING_WINDOW = "committee.proposalVotingWindow"; } diff --git a/common/src/main/java/org/tron/core/config/Parameter.java b/common/src/main/java/org/tron/core/config/Parameter.java index a71dc58e8bd..7d0681a9f91 100644 --- a/common/src/main/java/org/tron/core/config/Parameter.java +++ b/common/src/main/java/org/tron/core/config/Parameter.java @@ -26,7 +26,8 @@ public enum ForkBlockVersionEnum { VERSION_4_7_4(29, 1596780000000L, 80), VERSION_4_7_5(30, 1596780000000L, 80), VERSION_4_7_7(31, 1596780000000L, 80), - VERSION_4_8_0(32, 1596780000000L, 80); + VERSION_4_8_0(32, 1596780000000L, 80), + VERSION_4_8_1(33, 1596780000000L, 80); // if add a version, modify BLOCK_VERSION simultaneously @Getter @@ -75,7 +76,7 @@ public class ChainConstant { public static final int SINGLE_REPEAT = 1; public static final int BLOCK_FILLED_SLOTS_NUMBER = 128; public static final int MAX_FROZEN_NUMBER = 1; - public static final int BLOCK_VERSION = 32; + public static final int BLOCK_VERSION = 33; public static final long FROZEN_PERIOD = 86_400_000L; public static final long DELEGATE_PERIOD = 3 * 86_400_000L; public static final long TRX_PRECISION = 1000_000L; diff --git a/framework/src/main/java/org/tron/core/Wallet.java b/framework/src/main/java/org/tron/core/Wallet.java index 8dfb18331ff..ac6a17916b5 100755 --- a/framework/src/main/java/org/tron/core/Wallet.java +++ b/framework/src/main/java/org/tron/core/Wallet.java @@ -1387,6 +1387,11 @@ public Protocol.ChainParameters getChainParameters() { .setValue(dbManager.getDynamicPropertiesStore().getAllowTvmBlob()) .build()); + builder.addChainParameter(Protocol.ChainParameters.ChainParameter.newBuilder() + .setKey("getProposalVotingWindow") + .setValue(dbManager.getDynamicPropertiesStore().getProposalVotingWindow()) + .build()); + return builder.build(); } diff --git a/framework/src/main/java/org/tron/core/config/args/Args.java b/framework/src/main/java/org/tron/core/config/args/Args.java index 277b3e7bc79..3fd3bd0605e 100644 --- a/framework/src/main/java/org/tron/core/config/args/Args.java +++ b/framework/src/main/java/org/tron/core/config/args/Args.java @@ -4,6 +4,7 @@ import static org.tron.common.math.Maths.max; import static org.tron.common.math.Maths.min; import static org.tron.core.Constant.ADD_PRE_FIX_BYTE_MAINNET; +import static org.tron.core.Constant.DEFAULT_PROPOSAL_VOTING_WINDOW; import static org.tron.core.Constant.DYNAMIC_ENERGY_INCREASE_FACTOR_RANGE; import static org.tron.core.Constant.DYNAMIC_ENERGY_MAX_FACTOR_RANGE; import static org.tron.core.config.Parameter.ChainConstant.BLOCK_PRODUCE_TIMEOUT_PERCENT; @@ -247,6 +248,7 @@ public static void clearParam() { PARAMETER.consensusLogicOptimization = 0; PARAMETER.allowTvmCancun = 0; PARAMETER.allowTvmBlob = 0; + PARAMETER.proposalVotingWindow = DEFAULT_PROPOSAL_VOTING_WINDOW; } /** @@ -1297,6 +1299,10 @@ public static void setParam(final Config config) { config.hasPath(Constant.COMMITTEE_ALLOW_TVM_BLOB) ? config .getInt(Constant.COMMITTEE_ALLOW_TVM_BLOB) : 0; + PARAMETER.proposalVotingWindow = + config.hasPath(Constant.COMMITTEE_PROPOSAL_VOTING_WINDOW) ? config + .getInt(Constant.COMMITTEE_PROPOSAL_VOTING_WINDOW) : DEFAULT_PROPOSAL_VOTING_WINDOW; + logConfig(); } diff --git a/framework/src/main/java/org/tron/core/consensus/ProposalService.java b/framework/src/main/java/org/tron/core/consensus/ProposalService.java index b25f0d6fa8d..3c13cb09514 100644 --- a/framework/src/main/java/org/tron/core/consensus/ProposalService.java +++ b/framework/src/main/java/org/tron/core/consensus/ProposalService.java @@ -384,6 +384,10 @@ public static boolean process(Manager manager, ProposalCapsule proposalCapsule) manager.getDynamicPropertiesStore().saveAllowTvmBlob(entry.getValue()); break; } + case PROPOSAL_VOTING_WINDOW: { + manager.getDynamicPropertiesStore().saveProposalVotingWindow(entry.getValue()); + break; + } default: find = false; break; diff --git a/framework/src/test/java/org/tron/common/ParameterTest.java b/framework/src/test/java/org/tron/common/ParameterTest.java index b16be405f61..4cbd0bc002b 100644 --- a/framework/src/test/java/org/tron/common/ParameterTest.java +++ b/framework/src/test/java/org/tron/common/ParameterTest.java @@ -316,5 +316,7 @@ public void testCommonParameter() { assertEquals(1, parameter.getAllowEnergyAdjustment()); parameter.setMaxCreateAccountTxSize(1000); assertEquals(1000, parameter.getMaxCreateAccountTxSize()); + parameter.setProposalVotingWindow(86400L); + assertEquals(86400L, parameter.getProposalVotingWindow()); } } diff --git a/framework/src/test/java/org/tron/core/services/ProposalServiceTest.java b/framework/src/test/java/org/tron/core/services/ProposalServiceTest.java index 300a38a0916..9317d36c88d 100644 --- a/framework/src/test/java/org/tron/core/services/ProposalServiceTest.java +++ b/framework/src/test/java/org/tron/core/services/ProposalServiceTest.java @@ -1,7 +1,9 @@ package org.tron.core.services; +import static org.tron.core.Constant.MAX_PROPOSAL_VOTING_WINDOW; import static org.tron.core.utils.ProposalUtil.ProposalType.CONSENSUS_LOGIC_OPTIMIZATION; import static org.tron.core.utils.ProposalUtil.ProposalType.ENERGY_FEE; +import static org.tron.core.utils.ProposalUtil.ProposalType.PROPOSAL_VOTING_WINDOW; import static org.tron.core.utils.ProposalUtil.ProposalType.TRANSACTION_FEE; import static org.tron.core.utils.ProposalUtil.ProposalType.WITNESS_127_PAY_PER_BLOCK; @@ -13,6 +15,7 @@ import org.junit.BeforeClass; import org.junit.Test; import org.tron.common.BaseTest; +import org.tron.common.parameter.CommonParameter; import org.tron.core.Constant; import org.tron.core.capsule.ProposalCapsule; import org.tron.core.config.args.Args; @@ -131,4 +134,21 @@ public void testUpdateConsensusLogicOptimization() { Assert.assertTrue(dbManager.getDynamicPropertiesStore().disableJavaLangMath()); } + @Test + public void testProposalVotingWindow() { + long defaultWindow = dbManager.getDynamicPropertiesStore().getProposalVotingWindow(); + long proposalExpireTime = CommonParameter.getInstance().getProposalExpireTime(); + Assert.assertEquals(proposalExpireTime, defaultWindow * 3000); + + Proposal proposal = Proposal.newBuilder().putParameters(PROPOSAL_VOTING_WINDOW.getCode(), + MAX_PROPOSAL_VOTING_WINDOW).build(); + ProposalCapsule proposalCapsule = new ProposalCapsule(proposal); + proposalCapsule.setExpirationTime(1627279200000L); + boolean result = ProposalService.process(dbManager, proposalCapsule); + Assert.assertTrue(result); + + long window = dbManager.getDynamicPropertiesStore().getProposalVotingWindow(); + Assert.assertEquals(MAX_PROPOSAL_VOTING_WINDOW, window); + } + } \ No newline at end of file From 26d5fe43fbb253762a39695db68a3664322ef0fd Mon Sep 17 00:00:00 2001 From: chengtx01 Date: Tue, 22 Jul 2025 17:02:26 +0800 Subject: [PATCH 18/59] feat(net): add configuration for p2p message rate limit --- .../tron/common/parameter/CommonParameter.java | 9 +++++++++ common/src/main/java/org/tron/core/Constant.java | 3 +++ .../org/tron/core/exception/P2pException.java | 1 + .../main/java/org/tron/core/config/args/Args.java | 15 +++++++++++++++ .../org/tron/core/net/P2pEventHandlerImpl.java | 1 + .../messagehandler/FetchInvDataMsgHandler.java | 2 +- .../org/tron/core/net/peer/PeerConnection.java | 13 ++++++------- 7 files changed, 36 insertions(+), 8 deletions(-) diff --git a/common/src/main/java/org/tron/common/parameter/CommonParameter.java b/common/src/main/java/org/tron/common/parameter/CommonParameter.java index 45893970fb0..fcf3e719d3a 100644 --- a/common/src/main/java/org/tron/common/parameter/CommonParameter.java +++ b/common/src/main/java/org/tron/common/parameter/CommonParameter.java @@ -429,6 +429,15 @@ public class CommonParameter { @Getter public int rateLimiterGlobalApiQps; @Getter + @Setter + public double rateLimiterSyncBlockChain; + @Getter + @Setter + public double rateLimiterFetchInvData; + @Getter + @Setter + public double rateLimiterDisconnect; + @Getter public DbBackupConfig dbBackupConfig; @Getter public RocksDbSettings rocksDBCustomSettings; diff --git a/common/src/main/java/org/tron/core/Constant.java b/common/src/main/java/org/tron/core/Constant.java index c5a8a02fb4e..f1701e830ed 100644 --- a/common/src/main/java/org/tron/core/Constant.java +++ b/common/src/main/java/org/tron/core/Constant.java @@ -318,6 +318,9 @@ public class Constant { public static final String RATE_LIMITER_HTTP = "rate.limiter.http"; public static final String RATE_LIMITER_RPC = "rate.limiter.rpc"; + public static final String RATE_LIMITER_P2P_SYNC_BLOCK_CHAIN = "rate.limiter.p2p.syncBlockChain"; + public static final String RATE_LIMITER_P2P_FETCH_INV_DATA = "rate.limiter.p2p.fetchInvData"; + public static final String RATE_LIMITER_P2P_DISCONNECT = "rate.limiter.p2p.disconnect"; public static final String SEED_NODE_IP_LIST = "seed.node.ip.list"; public static final String NODE_METRICS_ENABLE = "node.metricsEnable"; diff --git a/common/src/main/java/org/tron/core/exception/P2pException.java b/common/src/main/java/org/tron/core/exception/P2pException.java index 00d82e9fbf7..eae830627c2 100644 --- a/common/src/main/java/org/tron/core/exception/P2pException.java +++ b/common/src/main/java/org/tron/core/exception/P2pException.java @@ -52,6 +52,7 @@ public enum TypeEnum { PROTOBUF_ERROR(14, "protobuf inconsistent"), BLOCK_SIGN_ERROR(15, "block sign error"), BLOCK_MERKLE_ERROR(16, "block merkle error"), + RATE_LIMIT_EXCEEDED(17, "rate limit exceeded"), DEFAULT(100, "default exception"); diff --git a/framework/src/main/java/org/tron/core/config/args/Args.java b/framework/src/main/java/org/tron/core/config/args/Args.java index 3162360bbb9..158aa5ba9c3 100644 --- a/framework/src/main/java/org/tron/core/config/args/Args.java +++ b/framework/src/main/java/org/tron/core/config/args/Args.java @@ -235,6 +235,9 @@ public static void clearParam() { PARAMETER.rateLimiterGlobalQps = 50000; PARAMETER.rateLimiterGlobalIpQps = 10000; PARAMETER.rateLimiterGlobalApiQps = 1000; + PARAMETER.rateLimiterSyncBlockChain = 3.0; + PARAMETER.rateLimiterFetchInvData = 3.0; + PARAMETER.rateLimiterDisconnect = 1.0; PARAMETER.p2pDisable = false; PARAMETER.dynamicConfigEnable = false; PARAMETER.dynamicConfigCheckInterval = 600; @@ -1041,6 +1044,18 @@ public static void setParam(final Config config) { PARAMETER.rateLimiterInitialization = getRateLimiterFromConfig(config); + PARAMETER.rateLimiterSyncBlockChain = + config.hasPath(Constant.RATE_LIMITER_P2P_SYNC_BLOCK_CHAIN) ? config + .getDouble(Constant.RATE_LIMITER_P2P_SYNC_BLOCK_CHAIN) : 3.0; + + PARAMETER.rateLimiterFetchInvData = + config.hasPath(Constant.RATE_LIMITER_P2P_FETCH_INV_DATA) ? config + .getDouble(Constant.RATE_LIMITER_P2P_FETCH_INV_DATA) : 3.0; + + PARAMETER.rateLimiterDisconnect = + config.hasPath(Constant.RATE_LIMITER_P2P_DISCONNECT) ? config + .getDouble(Constant.RATE_LIMITER_P2P_DISCONNECT) : 1.0; + PARAMETER.changedDelegation = config.hasPath(Constant.COMMITTEE_CHANGED_DELEGATION) ? config .getInt(Constant.COMMITTEE_CHANGED_DELEGATION) : 0; diff --git a/framework/src/main/java/org/tron/core/net/P2pEventHandlerImpl.java b/framework/src/main/java/org/tron/core/net/P2pEventHandlerImpl.java index 90c3739a3ec..2c631052b9d 100644 --- a/framework/src/main/java/org/tron/core/net/P2pEventHandlerImpl.java +++ b/framework/src/main/java/org/tron/core/net/P2pEventHandlerImpl.java @@ -261,6 +261,7 @@ private void processException(PeerConnection peer, TronMessage msg, Exception ex code = Protocol.ReasonCode.NO_SUCH_MESSAGE; break; case BAD_MESSAGE: + case RATE_LIMIT_EXCEEDED: code = Protocol.ReasonCode.BAD_PROTOCOL; break; case SYNC_FAILED: diff --git a/framework/src/main/java/org/tron/core/net/messagehandler/FetchInvDataMsgHandler.java b/framework/src/main/java/org/tron/core/net/messagehandler/FetchInvDataMsgHandler.java index 30e200fc8e1..d2b1a82e8db 100644 --- a/framework/src/main/java/org/tron/core/net/messagehandler/FetchInvDataMsgHandler.java +++ b/framework/src/main/java/org/tron/core/net/messagehandler/FetchInvDataMsgHandler.java @@ -157,7 +157,7 @@ private void check(PeerConnection peer, FetchInvDataMessage fetchInvDataMsg) thr throw new P2pException(TypeEnum.BAD_MESSAGE, "no need sync"); } if (!peer.getP2pRateLimiter().tryAcquire(fetchInvDataMsg.getType().asByte())) { - throw new P2pException(TypeEnum.BAD_MESSAGE, fetchInvDataMsg.getType() + throw new P2pException(TypeEnum.RATE_LIMIT_EXCEEDED, fetchInvDataMsg.getType() + " message exceeds the rate limit"); } if (fetchInvDataMsg.getHashList().size() > NetConstants.MAX_BLOCK_FETCH_PER_PEER) { diff --git a/framework/src/main/java/org/tron/core/net/peer/PeerConnection.java b/framework/src/main/java/org/tron/core/net/peer/PeerConnection.java index 1d8cff3b060..bff94d8ab42 100644 --- a/framework/src/main/java/org/tron/core/net/peer/PeerConnection.java +++ b/framework/src/main/java/org/tron/core/net/peer/PeerConnection.java @@ -59,10 +59,6 @@ public class PeerConnection { private static List relayNodes = Args.getInstance().getFastForwardNodes(); - private static final double SYNC_BLOCK_CHAIN_RATE = 3.0; - private static final double FETCH_INV_DATA_RATE = 3.0; - private static final double P2P_DISCONNECT_RATE = 1.0; - @Getter private PeerStatistics peerStatistics = new PeerStatistics(); @@ -175,9 +171,12 @@ public void setChannel(Channel channel) { } this.nodeStatistics = TronStatsManager.getNodeStatistics(channel.getInetAddress()); lastInteractiveTime = System.currentTimeMillis(); - p2pRateLimiter.register(SYNC_BLOCK_CHAIN.asByte(), SYNC_BLOCK_CHAIN_RATE); - p2pRateLimiter.register(FETCH_INV_DATA.asByte(), FETCH_INV_DATA_RATE); - p2pRateLimiter.register(P2P_DISCONNECT.asByte(), P2P_DISCONNECT_RATE); + p2pRateLimiter.register(SYNC_BLOCK_CHAIN.asByte(), + Args.getInstance().getRateLimiterSyncBlockChain()); + p2pRateLimiter.register(FETCH_INV_DATA.asByte(), + Args.getInstance().getRateLimiterFetchInvData()); + p2pRateLimiter.register(P2P_DISCONNECT.asByte(), + Args.getInstance().getRateLimiterDisconnect()); } public void setBlockBothHave(BlockId blockId) { From 12e2faf8ea92f38e2feec779bad2db26db163dcd Mon Sep 17 00:00:00 2001 From: halibobo1205 Date: Thu, 22 May 2025 14:59:27 +0800 Subject: [PATCH 19/59] feat(config): sync config with tron-deployment 1. add missed config 2. update seed ip list --- framework/src/main/resources/config.conf | 165 +++++++++++++++++------ 1 file changed, 122 insertions(+), 43 deletions(-) diff --git a/framework/src/main/resources/config.conf b/framework/src/main/resources/config.conf index d434d9c7203..7603ef6ffbb 100644 --- a/framework/src/main/resources/config.conf +++ b/framework/src/main/resources/config.conf @@ -101,7 +101,7 @@ storage { # data root setting, for check data, currently, only reward-vi is used. # merkleRoot = { - # reward-vi = 9debcb9924055500aaae98cdee10501c5c39d4daa75800a996f4bdda73dbccd8 // main-net, Sha256Hash, hexString + # reward-vi = 9debcb9924055500aaae98cdee10501c5c39d4daa75800a996f4bdda73dbccd8 // main-net, Sha256Hash, hexString # } } @@ -119,13 +119,13 @@ node.discovery = { #} node.backup { + # udp listen port, each member should have the same configuration + port = 10001 + # my priority, each member should use different priority priority = 8 - # udp listen port, each member should have the save configuration - port = 10001 - - # time interval to send keepAlive message, each member should have the save configuration + # time interval to send keepAlive message, each member should have the same configuration keepAliveInterval = 3000 # peer's ip list, can't contain mine @@ -166,12 +166,14 @@ node { udpNettyWorkThreadNum = 1 + # Number of validate sign thread, default availableProcessors + # validateSignThreadNum = 16 + maxConnections = 30 + minConnections = 8 - minActiveConnections = 3 - # Number of validate sign thread, default availableProcessors / 2 - # validateSignThreadNum = 16 + minActiveConnections = 3 maxConnectionsWithSameIp = 2 @@ -214,19 +216,6 @@ node { PBFTPort = 8092 } - # use your ipv6 address for node discovery and tcp connection, default false - enableIpv6 = false - - # if your node's highest block num is below than all your pees', try to acquire new connection. default false - effectiveCheckEnable = false - - dns { - # dns urls to get nodes, url format tree://{pubkey}@{domain}, default empty - treeUrls = [ - #"tree://AKMQMNAJJBL73LXWPXDI4I5ZWWIZ4AWO34DWQ636QOBBXNFXH3LQS@main.trondisco.net", - ] - } - rpc { enable = true port = 50051 @@ -234,6 +223,7 @@ node { solidityPort = 50061 PBFTEnable = true PBFTPort = 50071 + # Number of gRPC thread, default availableProcessors / 2 # thread = 16 @@ -274,6 +264,74 @@ node { # Limits the maximum number (default 700) of transaction from network layer # netMaxTrxPerSecond = 700 + # Whether to enable the node detection function, default false + # nodeDetectEnable = false + + # use your ipv6 address for node discovery and tcp connection, default false + # enableIpv6 = false + + # if your node's highest block num is below than all your pees', try to acquire new connection. default false + # effectiveCheckEnable = false + + # Dynamic loading configuration function, disabled by default + # dynamicConfig = { + # enable = false + # Configuration file change check interval, default is 600 seconds + # checkInterval = 600 + # } + + dns { + # dns urls to get nodes, url format tree://{pubkey}@{domain}, default empty + treeUrls = [ + #"tree://AKMQMNAJJBL73LXWPXDI4I5ZWWIZ4AWO34DWQ636QOBBXNFXH3LQS@main.trondisco.net", + ] + + # enable or disable dns publish, default false + # publish = false + + # dns domain to publish nodes, required if publish is true + # dnsDomain = "nodes1.example.org" + + # dns private key used to publish, required if publish is true, hex string of length 64 + # dnsPrivate = "b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291" + + # known dns urls to publish if publish is true, url format tree://{pubkey}@{domain}, default empty + # knownUrls = [ + #"tree://APFGGTFOBVE2ZNAB3CSMNNX6RRK3ODIRLP2AA5U4YFAA6MSYZUYTQ@nodes2.example.org", + # ] + + # staticNodes = [ + # static nodes to published on dns + # Sample entries: + # "ip:port", + # "ip:port" + # ] + + # merge several nodes into a leaf of tree, should be 1~5 + # maxMergeSize = 5 + + # only nodes change percent is bigger then the threshold, we update data on dns + # changeThreshold = 0.1 + + # dns server to publish, required if publish is true, only aws or aliyun is support + # serverType = "aws" + + # access key id of aws or aliyun api, required if publish is true, string + # accessKeyId = "your-key-id" + + # access key secret of aws or aliyun api, required if publish is true, string + # accessKeySecret = "your-key-secret" + + # if publish is true and serverType is aliyun, it's endpoint of aws dns server, string + # aliyunDnsEndpoint = "alidns.aliyuncs.com" + + # if publish is true and serverType is aws, it's region of aws api, such as "eu-south-1", string + # awsRegion = "us-east-1" + + # if publish is true and server-type is aws, it's host zone id of aws's domain, string + # awsHostZoneId = "your-host-zone-id" + } + # open the history query APIs(http&GRPC) when node is a lite fullNode, # like {getBlockByNum, getBlockByID, getTransactionByID...}. # default: false. @@ -294,11 +352,11 @@ node { # The maximum blocks range to retrieve logs for eth_getLogs, default value is 5000, # should be > 0, otherwise means no limit. - # maxBlockRange = 5000 + maxBlockRange = 5000 # The maximum number of allowed topics within a topic criteria, default value is 1000, # should be > 0, otherwise means no limit. - # maxSubTopics = 1000 + maxSubTopics = 1000 } # Disabled api list, it will work for http, rpc and pbft, both fullnode and soliditynode, @@ -319,7 +377,7 @@ rate.limiter = { # before do the request and release the resource after got the reponse automaticlly. permit should be a Integer. # QpsRateLimiterAdapter: qps is the average request count in one second supported by the server, it could be a Double or a Integer. # IPQPSRateLimiterAdapter: similar to the QpsRateLimiterAdapter, qps could be a Double or a Integer. - # If do not set, the "default strategy" is set.The "default startegy" is based on QpsRateLimiterAdapter, the qps is set as 10000. + # If do not set, the "default strategy" is set.The "default startegy" is based on QpsRateLimiterAdapter, the qps is set as 1000. # # Sample entries: # @@ -363,9 +421,14 @@ rate.limiter = { # }, ] + # global qps, default 50000 + # global.qps = 50000 + # IP-based global qps, default 10000 + # global.ip.qps = 10000 } + seed.node = { # List of the seed nodes # Seed nodes are stable full nodes @@ -375,18 +438,18 @@ seed.node = { # "ip:port" # ] ip.list = [ - "54.236.37.243:18888", - "52.53.189.99:18888", - "18.196.99.16:18888", - "34.253.187.192:18888", - "52.56.56.149:18888", - "35.180.51.163:18888", - "54.252.224.209:18888", - "18.228.15.36:18888", - "52.15.93.92:18888", - "34.220.77.106:18888", - "13.127.47.162:18888", - "13.124.62.58:18888", + "3.225.171.164:18888", + "52.8.46.215:18888", + "3.79.71.167:18888", + "108.128.110.16:18888", + "18.133.82.227:18888", + "35.180.81.133:18888", + "13.210.151.5:18888", + "18.231.27.82:18888", + "3.12.212.122:18888", + "52.24.128.7:18888", + "15.207.144.3:18888", + "3.39.38.55:18888", "54.151.226.240:18888", "35.174.93.198:18888", "18.210.241.149:18888", @@ -404,7 +467,9 @@ seed.node = { "54.82.161.39:18888", "54.179.207.68:18888", "18.142.82.44:18888", - "18.163.230.203:18888" + "18.163.230.203:18888", + # "[2a05:d014:1f2f:2600:1b15:921:d60b:4c60]:18888", // use this if support ipv6 + # "[2600:1f18:7260:f400:8947:ebf3:78a0:282b]:18888", // use this if support ipv6 ] } @@ -595,7 +660,7 @@ block = { proposalExpireTime = 259200000 // 3 day: 259200000(ms) } -# Transaction reference block, default is "solid", configure to "head" may accur TaPos error +# Transaction reference block, default is "solid", configure to "head" may cause TaPos error # trx.reference.block = "solid" // head;solid; # This property sets the number of milliseconds after the creation of the transaction that is expired, default value is 60000. @@ -611,7 +676,8 @@ vm = { # Indicates whether the node stores featured internal transactions, such as freeze, vote and so on # saveFeaturedInternalTx = false - # Indicates whether the node stores the details of the internal transactions generated by the CANCELALLUNFREEZEV2 opcode, such as bandwidth/energy/tronpower cancel amount. + # Indicates whether the node stores the details of the internal transactions generated by the + # CANCELALLUNFREEZEV2 opcode, such as bandwidth/energy/tronpower cancel amount. # saveCancelAllUnfreezeV2Details = false # In rare cases, transactions that will be within the specified maximum execution time (default 10(ms)) are re-executed and packaged @@ -635,21 +701,32 @@ event.subscribe = { bindport = 5555 // bind port sendqueuelength = 1000 //max length of send queue } + version = 0 + # Specify the starting block number to sync historical events. This is only applicable when version = 1. + # After performing a full event sync, set this value to 0 or a negative number. + # startSyncBlockNum = 1 path = "" // absolute path of plugin server = "" // target server address to receive event triggers - dbconfig = "" // dbname|username|password - contractParse = true, + // dbname|username|password, if you want to create indexes for collections when the collections + // are not exist, you can add version and set it to 2, as dbname|username|password|version + // if you use version 2 and one collection not exists, it will create index automaticaly; + // if you use version 2 and one collection exists, it will not create index, you must create index manually; + dbconfig = "" + contractParse = true topics = [ { triggerName = "block" // block trigger, the value can't be modified enable = false topic = "block" // plugin topic, the value could be modified + solidified = false // if set true, just need solidified block, default is false }, { triggerName = "transaction" enable = false topic = "transaction" + solidified = false + ethCompatible = false // if set true, add transactionIndex, cumulativeEnergyUsed, preCumulativeLogCount, logList, energyUnitPrice, default is false }, { triggerName = "contractevent" @@ -660,9 +737,10 @@ event.subscribe = { triggerName = "contractlog" enable = false topic = "contractlog" + redundancy = false // if set true, contractevent will also be regarded as contractlog }, { - triggerName = "solidity" // solidity block event trigger, the value can't be modified + triggerName = "solidity" // solidity block trigger(just include solidity block number and timestamp), the value can't be modified enable = true // the default value is true topic = "solidity" }, @@ -675,6 +753,7 @@ event.subscribe = { triggerName = "soliditylog" enable = false topic = "soliditylog" + redundancy = false // if set true, solidityevent will also be regarded as soliditylog } ] @@ -689,5 +768,5 @@ event.subscribe = { "" // contract topic you want to subscribe, if it's set to "", you will receive contract logs/events with any contract topic. ] } - } + From 46b1c0935cc8f0a434841d8b6a25c58af0f12d24 Mon Sep 17 00:00:00 2001 From: chengtx01 Date: Tue, 5 Aug 2025 11:54:12 +0800 Subject: [PATCH 20/59] fix(log): fix log issue --- .../tron/core/net/messagehandler/FetchInvDataMsgHandler.java | 2 +- .../core/net/messagehandler/FetchInvDataMsgHandlerTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/src/main/java/org/tron/core/net/messagehandler/FetchInvDataMsgHandler.java b/framework/src/main/java/org/tron/core/net/messagehandler/FetchInvDataMsgHandler.java index d2b1a82e8db..38cf3f2a0e2 100644 --- a/framework/src/main/java/org/tron/core/net/messagehandler/FetchInvDataMsgHandler.java +++ b/framework/src/main/java/org/tron/core/net/messagehandler/FetchInvDataMsgHandler.java @@ -161,7 +161,7 @@ private void check(PeerConnection peer, FetchInvDataMessage fetchInvDataMsg) thr + " message exceeds the rate limit"); } if (fetchInvDataMsg.getHashList().size() > NetConstants.MAX_BLOCK_FETCH_PER_PEER) { - throw new P2pException(TypeEnum.BAD_MESSAGE, "fetch too more blocks, size:" + throw new P2pException(TypeEnum.BAD_MESSAGE, "fetch too many blocks, size:" + fetchInvDataMsg.getHashList().size()); } for (Sha256Hash hash : fetchInvDataMsg.getHashList()) { diff --git a/framework/src/test/java/org/tron/core/net/messagehandler/FetchInvDataMsgHandlerTest.java b/framework/src/test/java/org/tron/core/net/messagehandler/FetchInvDataMsgHandlerTest.java index acee2d3077d..43036ce142a 100644 --- a/framework/src/test/java/org/tron/core/net/messagehandler/FetchInvDataMsgHandlerTest.java +++ b/framework/src/test/java/org/tron/core/net/messagehandler/FetchInvDataMsgHandlerTest.java @@ -126,7 +126,7 @@ public void testRateLimiter() { try { fetchInvDataMsgHandler.processMessage(peer, msg); } catch (Exception e) { - Assert.assertEquals("fetch too more blocks, size:101", e.getMessage()); + Assert.assertEquals("fetch too many blocks, size:101", e.getMessage()); } try { fetchInvDataMsgHandler.processMessage(peer, msg); From e3f9b526425f94589b6a1c0276984eee67baff13 Mon Sep 17 00:00:00 2001 From: liuxincheng Date: Fri, 1 Aug 2025 17:41:23 +0800 Subject: [PATCH 21/59] feat(conf): make compatible with old configurations --- .../core/actuator/ProposalCreateActuator.java | 4 +-- .../org/tron/core/utils/ProposalUtil.java | 20 +++++------ .../core/store/DynamicPropertiesStore.java | 15 +++++---- .../common/parameter/CommonParameter.java | 8 ----- .../src/main/java/org/tron/core/Constant.java | 11 +++---- .../src/main/java/org/tron/core/Wallet.java | 4 +-- .../java/org/tron/core/config/args/Args.java | 33 ++++++++++++++----- .../tron/core/consensus/ProposalService.java | 4 +-- .../java/org/tron/common/ParameterTest.java | 2 -- .../core/services/ProposalServiceTest.java | 18 +++++----- 10 files changed, 61 insertions(+), 58 deletions(-) diff --git a/actuator/src/main/java/org/tron/core/actuator/ProposalCreateActuator.java b/actuator/src/main/java/org/tron/core/actuator/ProposalCreateActuator.java index 41c501bfff0..e0044c4958d 100755 --- a/actuator/src/main/java/org/tron/core/actuator/ProposalCreateActuator.java +++ b/actuator/src/main/java/org/tron/core/actuator/ProposalCreateActuator.java @@ -3,7 +3,6 @@ import static org.tron.core.actuator.ActuatorConstant.ACCOUNT_EXCEPTION_STR; import static org.tron.core.actuator.ActuatorConstant.NOT_EXIST_STR; import static org.tron.core.actuator.ActuatorConstant.WITNESS_EXCEPTION_STR; -import static org.tron.core.config.Parameter.ChainConstant.BLOCK_PRODUCED_INTERVAL; import com.google.protobuf.ByteString; import com.google.protobuf.InvalidProtocolBufferException; @@ -53,8 +52,7 @@ public boolean execute(Object result) throws ContractExeException { long currentMaintenanceTime = chainBaseManager.getDynamicPropertiesStore().getNextMaintenanceTime(); - long now3 = now + chainBaseManager.getDynamicPropertiesStore().getProposalVotingWindow() - * BLOCK_PRODUCED_INTERVAL; + long now3 = now + chainBaseManager.getDynamicPropertiesStore().getProposalExpireTime(); long round = (now3 - currentMaintenanceTime) / maintenanceTimeInterval; long expirationTime = currentMaintenanceTime + (round + 1) * maintenanceTimeInterval; diff --git a/actuator/src/main/java/org/tron/core/utils/ProposalUtil.java b/actuator/src/main/java/org/tron/core/utils/ProposalUtil.java index 053146bb9ac..1df35154d7d 100644 --- a/actuator/src/main/java/org/tron/core/utils/ProposalUtil.java +++ b/actuator/src/main/java/org/tron/core/utils/ProposalUtil.java @@ -4,8 +4,8 @@ import static org.tron.core.Constant.CREATE_ACCOUNT_TRANSACTION_MIN_BYTE_SIZE; import static org.tron.core.Constant.DYNAMIC_ENERGY_INCREASE_FACTOR_RANGE; import static org.tron.core.Constant.DYNAMIC_ENERGY_MAX_FACTOR_RANGE; -import static org.tron.core.Constant.MAX_PROPOSAL_VOTING_WINDOW; -import static org.tron.core.Constant.MIN_PROPOSAL_VOTING_WINDOW; +import static org.tron.core.Constant.MAX_PROPOSAL_EXPIRE_TIME; +import static org.tron.core.Constant.MIN_PROPOSAL_EXPIRE_TIME; import static org.tron.core.config.Parameter.ChainConstant.ONE_YEAR_BLOCK_NUMBERS; import org.tron.common.utils.ForkController; @@ -841,17 +841,17 @@ public static void validator(DynamicPropertiesStore dynamicPropertiesStore, } break; } - case PROPOSAL_VOTING_WINDOW: { + case PROPOSAL_EXPIRE_TIME: { if (!forkController.pass(ForkBlockVersionEnum.VERSION_4_8_1)) { throw new ContractValidateException( - "Bad chain parameter id [PROPOSAL_VOTING_WINDOW]"); + "Bad chain parameter id [PROPOSAL_EXPIRE_TIME]"); } - if (value <= MIN_PROPOSAL_VOTING_WINDOW - || value > MAX_PROPOSAL_VOTING_WINDOW) { + if (value <= MIN_PROPOSAL_EXPIRE_TIME + || value >= MAX_PROPOSAL_EXPIRE_TIME) { throw new ContractValidateException( - "This value[PROPOSAL_VOTING_WINDOW] is only allowed to be greater than " - + MIN_PROPOSAL_VOTING_WINDOW + " and less than or equal to " - + MAX_PROPOSAL_VOTING_WINDOW + "!"); + "This value[PROPOSAL_EXPIRE_TIME] is only allowed to be greater than " + + MIN_PROPOSAL_EXPIRE_TIME + " and less than " + + MAX_PROPOSAL_EXPIRE_TIME + "!"); } break; } @@ -938,7 +938,7 @@ public enum ProposalType { // current value, value range ALLOW_STRICT_MATH(87), // 0, 1 CONSENSUS_LOGIC_OPTIMIZATION(88), // 0, 1 ALLOW_TVM_BLOB(89), // 0, 1 - PROPOSAL_VOTING_WINDOW(92); // (0, 10512000] + PROPOSAL_EXPIRE_TIME(92); // (0, 31536003000) private long code; diff --git a/chainbase/src/main/java/org/tron/core/store/DynamicPropertiesStore.java b/chainbase/src/main/java/org/tron/core/store/DynamicPropertiesStore.java index 81ae65dd880..bee13782e53 100644 --- a/chainbase/src/main/java/org/tron/core/store/DynamicPropertiesStore.java +++ b/chainbase/src/main/java/org/tron/core/store/DynamicPropertiesStore.java @@ -1,6 +1,8 @@ package org.tron.core.store; import static org.tron.common.math.Maths.max; +import static org.tron.core.Constant.MAX_PROPOSAL_EXPIRE_TIME; +import static org.tron.core.Constant.MIN_PROPOSAL_EXPIRE_TIME; import static org.tron.core.config.Parameter.ChainConstant.BLOCK_PRODUCED_INTERVAL; import static org.tron.core.config.Parameter.ChainConstant.DELEGATE_PERIOD; @@ -231,7 +233,7 @@ public class DynamicPropertiesStore extends TronStoreWithRevoking private static final byte[] ALLOW_TVM_CANCUN = "ALLOW_TVM_CANCUN".getBytes(); private static final byte[] ALLOW_TVM_BLOB = "ALLOW_TVM_BLOB".getBytes(); - private static final byte[] PROPOSAL_VOTING_WINDOW = "PROPOSAL_VOTING_WINDOW".getBytes(); + private static final byte[] PROPOSAL_EXPIRE_TIME = "PROPOSAL_EXPIRE_TIME".getBytes(); @Autowired private DynamicPropertiesStore(@Value("properties") String dbName) { @@ -2947,15 +2949,16 @@ public long getAllowTvmBlob() { .orElse(CommonParameter.getInstance().getAllowTvmBlob()); } - public void saveProposalVotingWindow(long proposalVotingWindow) { - this.put(PROPOSAL_VOTING_WINDOW, new BytesCapsule(ByteArray.fromLong(proposalVotingWindow))); + public void saveProposalExpireTime(long proposalExpireTime) { + this.put(PROPOSAL_EXPIRE_TIME, new BytesCapsule(ByteArray.fromLong(proposalExpireTime))); } - public long getProposalVotingWindow() { - return Optional.ofNullable(getUnchecked(PROPOSAL_VOTING_WINDOW)) + public long getProposalExpireTime() { + return Optional.ofNullable(getUnchecked(PROPOSAL_EXPIRE_TIME)) .map(BytesCapsule::getData) .map(ByteArray::toLong) - .orElse(CommonParameter.getInstance().getProposalVotingWindow()); + .filter(time -> time > MIN_PROPOSAL_EXPIRE_TIME && time < MAX_PROPOSAL_EXPIRE_TIME) + .orElse(CommonParameter.getInstance().getProposalExpireTime()); } private static class DynamicResourceProperties { diff --git a/common/src/main/java/org/tron/common/parameter/CommonParameter.java b/common/src/main/java/org/tron/common/parameter/CommonParameter.java index a06e31a9262..45893970fb0 100644 --- a/common/src/main/java/org/tron/common/parameter/CommonParameter.java +++ b/common/src/main/java/org/tron/common/parameter/CommonParameter.java @@ -724,14 +724,6 @@ public class CommonParameter { @Setter public long allowTvmBlob; - /** - * Committee parameter. - * Indicate the number of blocks after which the proposal approval window will take effect. - */ - @Getter - @Setter - public long proposalVotingWindow; - private static double calcMaxTimeRatio() { //return max(2.0, min(5.0, 5 * 4.0 / max(Runtime.getRuntime().availableProcessors(), 1))); return 5.0; diff --git a/common/src/main/java/org/tron/core/Constant.java b/common/src/main/java/org/tron/core/Constant.java index 612de614d28..9af085b0464 100644 --- a/common/src/main/java/org/tron/core/Constant.java +++ b/common/src/main/java/org/tron/core/Constant.java @@ -39,12 +39,9 @@ public class Constant { public static final long MAX_CONTRACT_RESULT_SIZE = 2L; public static final long PB_DEFAULT_ENERGY_LIMIT = 0L; public static final long CREATOR_DEFAULT_ENERGY_LIMIT = 1000 * 10_000L; - public static final long MIN_PROPOSAL_VOTING_WINDOW = 0L; // Number of blocks to 0 day - public static final long MAX_PROPOSAL_VOTING_WINDOW = 10512000L; // Number of blocks to 365 days - /** - * default number of blocks to 3 days - */ - public static final long DEFAULT_PROPOSAL_VOTING_WINDOW = 86400L; + public static final long MIN_PROPOSAL_EXPIRE_TIME = 0L; // 0 ms + public static final long MAX_PROPOSAL_EXPIRE_TIME = 31536003000L; // ms of 365 days + 3000 ms + public static final long DEFAULT_PROPOSAL_EXPIRE_TIME = 259200000L; // ms of 3 days // Numbers @@ -411,5 +408,5 @@ public class Constant { public static final String COMMITTEE_ALLOW_TVM_CANCUN = "committee.allowTvmCancun"; public static final String COMMITTEE_ALLOW_TVM_BLOB = "committee.allowTvmBlob"; - public static final String COMMITTEE_PROPOSAL_VOTING_WINDOW = "committee.proposalVotingWindow"; + public static final String COMMITTEE_PROPOSAL_EXPIRE_TIME = "committee.proposalExpireTime"; } diff --git a/framework/src/main/java/org/tron/core/Wallet.java b/framework/src/main/java/org/tron/core/Wallet.java index ac6a17916b5..20a4613295d 100755 --- a/framework/src/main/java/org/tron/core/Wallet.java +++ b/framework/src/main/java/org/tron/core/Wallet.java @@ -1388,8 +1388,8 @@ public Protocol.ChainParameters getChainParameters() { .build()); builder.addChainParameter(Protocol.ChainParameters.ChainParameter.newBuilder() - .setKey("getProposalVotingWindow") - .setValue(dbManager.getDynamicPropertiesStore().getProposalVotingWindow()) + .setKey("getProposalExpireTime") + .setValue(dbManager.getDynamicPropertiesStore().getProposalExpireTime()) .build()); return builder.build(); diff --git a/framework/src/main/java/org/tron/core/config/args/Args.java b/framework/src/main/java/org/tron/core/config/args/Args.java index 3fd3bd0605e..5b4178e6e4c 100644 --- a/framework/src/main/java/org/tron/core/config/args/Args.java +++ b/framework/src/main/java/org/tron/core/config/args/Args.java @@ -4,9 +4,11 @@ import static org.tron.common.math.Maths.max; import static org.tron.common.math.Maths.min; import static org.tron.core.Constant.ADD_PRE_FIX_BYTE_MAINNET; -import static org.tron.core.Constant.DEFAULT_PROPOSAL_VOTING_WINDOW; +import static org.tron.core.Constant.DEFAULT_PROPOSAL_EXPIRE_TIME; import static org.tron.core.Constant.DYNAMIC_ENERGY_INCREASE_FACTOR_RANGE; import static org.tron.core.Constant.DYNAMIC_ENERGY_MAX_FACTOR_RANGE; +import static org.tron.core.Constant.MAX_PROPOSAL_EXPIRE_TIME; +import static org.tron.core.Constant.MIN_PROPOSAL_EXPIRE_TIME; import static org.tron.core.config.Parameter.ChainConstant.BLOCK_PRODUCE_TIMEOUT_PERCENT; import static org.tron.core.config.Parameter.ChainConstant.MAX_ACTIVE_WITNESS_NUM; @@ -248,7 +250,6 @@ public static void clearParam() { PARAMETER.consensusLogicOptimization = 0; PARAMETER.allowTvmCancun = 0; PARAMETER.allowTvmBlob = 0; - PARAMETER.proposalVotingWindow = DEFAULT_PROPOSAL_VOTING_WINDOW; } /** @@ -817,9 +818,7 @@ public static void setParam(final Config config) { config.hasPath(Constant.BLOCK_MAINTENANCE_TIME_INTERVAL) ? config .getInt(Constant.BLOCK_MAINTENANCE_TIME_INTERVAL) : 21600000L; - PARAMETER.proposalExpireTime = - config.hasPath(Constant.BLOCK_PROPOSAL_EXPIRE_TIME) ? config - .getInt(Constant.BLOCK_PROPOSAL_EXPIRE_TIME) : 259200000L; + PARAMETER.proposalExpireTime = getProposalExpirationTime(config); PARAMETER.checkFrozenTime = config.hasPath(Constant.BLOCK_CHECK_FROZEN_TIME) ? config @@ -1299,13 +1298,29 @@ public static void setParam(final Config config) { config.hasPath(Constant.COMMITTEE_ALLOW_TVM_BLOB) ? config .getInt(Constant.COMMITTEE_ALLOW_TVM_BLOB) : 0; - PARAMETER.proposalVotingWindow = - config.hasPath(Constant.COMMITTEE_PROPOSAL_VOTING_WINDOW) ? config - .getInt(Constant.COMMITTEE_PROPOSAL_VOTING_WINDOW) : DEFAULT_PROPOSAL_VOTING_WINDOW; - logConfig(); } + private static long getProposalExpirationTime(final Config config) { + if (config.hasPath(Constant.COMMITTEE_PROPOSAL_EXPIRE_TIME)) { + throw new IllegalArgumentException("It is not allowed to configure " + + "commit.proposalExpireTime in config.conf, please set the value in " + + "block.proposalExpireTime."); + } + if (config.hasPath(Constant.BLOCK_PROPOSAL_EXPIRE_TIME)) { + long proposalExpireTime = config.getLong(Constant.BLOCK_PROPOSAL_EXPIRE_TIME); + if (proposalExpireTime <= MIN_PROPOSAL_EXPIRE_TIME + || proposalExpireTime >= MAX_PROPOSAL_EXPIRE_TIME) { + throw new IllegalArgumentException("The value[block.proposalExpireTime] is only allowed to " + + "be greater than " + MIN_PROPOSAL_EXPIRE_TIME + " and less than " + + MAX_PROPOSAL_EXPIRE_TIME + "!"); + } + return proposalExpireTime; + } else { + return DEFAULT_PROPOSAL_EXPIRE_TIME; + } + } + private static List getWitnessesFromConfig(final com.typesafe.config.Config config) { return config.getObjectList(Constant.GENESIS_BLOCK_WITNESSES).stream() .map(Args::createWitness) diff --git a/framework/src/main/java/org/tron/core/consensus/ProposalService.java b/framework/src/main/java/org/tron/core/consensus/ProposalService.java index 3c13cb09514..d6cf0e5fe67 100644 --- a/framework/src/main/java/org/tron/core/consensus/ProposalService.java +++ b/framework/src/main/java/org/tron/core/consensus/ProposalService.java @@ -384,8 +384,8 @@ public static boolean process(Manager manager, ProposalCapsule proposalCapsule) manager.getDynamicPropertiesStore().saveAllowTvmBlob(entry.getValue()); break; } - case PROPOSAL_VOTING_WINDOW: { - manager.getDynamicPropertiesStore().saveProposalVotingWindow(entry.getValue()); + case PROPOSAL_EXPIRE_TIME: { + manager.getDynamicPropertiesStore().saveProposalExpireTime(entry.getValue()); break; } default: diff --git a/framework/src/test/java/org/tron/common/ParameterTest.java b/framework/src/test/java/org/tron/common/ParameterTest.java index 4cbd0bc002b..b16be405f61 100644 --- a/framework/src/test/java/org/tron/common/ParameterTest.java +++ b/framework/src/test/java/org/tron/common/ParameterTest.java @@ -316,7 +316,5 @@ public void testCommonParameter() { assertEquals(1, parameter.getAllowEnergyAdjustment()); parameter.setMaxCreateAccountTxSize(1000); assertEquals(1000, parameter.getMaxCreateAccountTxSize()); - parameter.setProposalVotingWindow(86400L); - assertEquals(86400L, parameter.getProposalVotingWindow()); } } diff --git a/framework/src/test/java/org/tron/core/services/ProposalServiceTest.java b/framework/src/test/java/org/tron/core/services/ProposalServiceTest.java index 9317d36c88d..e81c75948b7 100644 --- a/framework/src/test/java/org/tron/core/services/ProposalServiceTest.java +++ b/framework/src/test/java/org/tron/core/services/ProposalServiceTest.java @@ -1,9 +1,9 @@ package org.tron.core.services; -import static org.tron.core.Constant.MAX_PROPOSAL_VOTING_WINDOW; +import static org.tron.core.Constant.MAX_PROPOSAL_EXPIRE_TIME; import static org.tron.core.utils.ProposalUtil.ProposalType.CONSENSUS_LOGIC_OPTIMIZATION; import static org.tron.core.utils.ProposalUtil.ProposalType.ENERGY_FEE; -import static org.tron.core.utils.ProposalUtil.ProposalType.PROPOSAL_VOTING_WINDOW; +import static org.tron.core.utils.ProposalUtil.ProposalType.PROPOSAL_EXPIRE_TIME; import static org.tron.core.utils.ProposalUtil.ProposalType.TRANSACTION_FEE; import static org.tron.core.utils.ProposalUtil.ProposalType.WITNESS_127_PAY_PER_BLOCK; @@ -135,20 +135,20 @@ public void testUpdateConsensusLogicOptimization() { } @Test - public void testProposalVotingWindow() { - long defaultWindow = dbManager.getDynamicPropertiesStore().getProposalVotingWindow(); + public void testProposalExpireTime() { + long defaultWindow = dbManager.getDynamicPropertiesStore().getProposalExpireTime(); long proposalExpireTime = CommonParameter.getInstance().getProposalExpireTime(); - Assert.assertEquals(proposalExpireTime, defaultWindow * 3000); + Assert.assertEquals(proposalExpireTime, defaultWindow); - Proposal proposal = Proposal.newBuilder().putParameters(PROPOSAL_VOTING_WINDOW.getCode(), - MAX_PROPOSAL_VOTING_WINDOW).build(); + Proposal proposal = Proposal.newBuilder().putParameters(PROPOSAL_EXPIRE_TIME.getCode(), + 31536000000L).build(); ProposalCapsule proposalCapsule = new ProposalCapsule(proposal); proposalCapsule.setExpirationTime(1627279200000L); boolean result = ProposalService.process(dbManager, proposalCapsule); Assert.assertTrue(result); - long window = dbManager.getDynamicPropertiesStore().getProposalVotingWindow(); - Assert.assertEquals(MAX_PROPOSAL_VOTING_WINDOW, window); + long window = dbManager.getDynamicPropertiesStore().getProposalExpireTime(); + Assert.assertEquals(MAX_PROPOSAL_EXPIRE_TIME - 3000, window); } } \ No newline at end of file From f8bd7cf02bc382f76feb057f3baa4c6825198dfd Mon Sep 17 00:00:00 2001 From: halibobo1205 Date: Wed, 6 Aug 2025 15:46:28 +0800 Subject: [PATCH 22/59] refactor(crypto,api): improve code quality resolve implicit narrowing conversion and information exposure issues --- .../tron/core/vm/PrecompiledContracts.java | 14 +++------- .../tron/core/capsule/TransactionCapsule.java | 11 +++----- .../tron/core/service/MortgageService.java | 2 +- .../main/java/org/tron/common/crypto/Rsv.java | 26 +++++++++++++++++++ .../java/org/tron/core/capsule/utils/RLP.java | 2 +- .../services/http/GetBrokerageServlet.java | 3 +-- .../core/services/http/GetRewardServlet.java | 3 +-- .../GetTransactionByIdSolidityServlet.java | 14 ++-------- ...GetTransactionInfoByIdSolidityServlet.java | 15 ++--------- .../jsonrpc/types/TransactionResult.java | 16 ++++-------- .../tron/core/zen/address/SpendingKey.java | 2 +- .../utils/client/utils/TransactionUtils.java | 15 +---------- 12 files changed, 47 insertions(+), 76 deletions(-) create mode 100644 crypto/src/main/java/org/tron/common/crypto/Rsv.java diff --git a/actuator/src/main/java/org/tron/core/vm/PrecompiledContracts.java b/actuator/src/main/java/org/tron/core/vm/PrecompiledContracts.java index 7913d7928fa..db6b22fb339 100644 --- a/actuator/src/main/java/org/tron/core/vm/PrecompiledContracts.java +++ b/actuator/src/main/java/org/tron/core/vm/PrecompiledContracts.java @@ -41,6 +41,7 @@ import org.apache.commons.lang3.tuple.Triple; import org.tron.common.crypto.Blake2bfMessageDigest; import org.tron.common.crypto.Hash; +import org.tron.common.crypto.Rsv; import org.tron.common.crypto.SignUtils; import org.tron.common.crypto.SignatureInterface; import org.tron.common.crypto.zksnark.BN128; @@ -352,22 +353,13 @@ private static byte[] encodeMultiRes(byte[]... words) { } private static byte[] recoverAddrBySign(byte[] sign, byte[] hash) { - byte v; - byte[] r; - byte[] s; byte[] out = null; if (ArrayUtils.isEmpty(sign) || sign.length < 65) { return new byte[0]; } try { - r = Arrays.copyOfRange(sign, 0, 32); - s = Arrays.copyOfRange(sign, 32, 64); - v = sign[64]; - if (v < 27) { - v += 27; - } - - SignatureInterface signature = SignUtils.fromComponents(r, s, v, + Rsv rsv = Rsv.fromSignature(sign); + SignatureInterface signature = SignUtils.fromComponents(rsv.getR(), rsv.getS(), rsv.getV(), CommonParameter.getInstance().isECKeyCryptoEngine()); if (signature.validateComponents()) { out = SignUtils.signatureToAddress(hash, signature, diff --git a/chainbase/src/main/java/org/tron/core/capsule/TransactionCapsule.java b/chainbase/src/main/java/org/tron/core/capsule/TransactionCapsule.java index 95f436b19f0..b11c6b1e0a4 100755 --- a/chainbase/src/main/java/org/tron/core/capsule/TransactionCapsule.java +++ b/chainbase/src/main/java/org/tron/core/capsule/TransactionCapsule.java @@ -41,6 +41,7 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.ArrayUtils; import org.tron.common.crypto.ECKey.ECDSASignature; +import org.tron.common.crypto.Rsv; import org.tron.common.crypto.SignInterface; import org.tron.common.crypto.SignUtils; import org.tron.common.es.ExecutorServiceManager; @@ -456,14 +457,8 @@ public static long getCallValue(Transaction.Contract contract) { } public static String getBase64FromByteString(ByteString sign) { - byte[] r = sign.substring(0, 32).toByteArray(); - byte[] s = sign.substring(32, 64).toByteArray(); - byte v = sign.byteAt(64); - if (v < 27) { - v += 27; //revId -> v - } - ECDSASignature signature = ECDSASignature.fromComponents(r, s, v); - return signature.toBase64(); + Rsv rsv = Rsv.fromSignature(sign.toByteArray()); + return ECDSASignature.fromComponents(rsv.getR(), rsv.getS(), rsv.getV()).toBase64(); } public static boolean validateSignature(Transaction transaction, diff --git a/chainbase/src/main/java/org/tron/core/service/MortgageService.java b/chainbase/src/main/java/org/tron/core/service/MortgageService.java index b1fd279c2db..f0182503ae8 100644 --- a/chainbase/src/main/java/org/tron/core/service/MortgageService.java +++ b/chainbase/src/main/java/org/tron/core/service/MortgageService.java @@ -182,7 +182,7 @@ private long computeReward(long cycle, List> votes) { } long userVote = vote.getValue(); double voteRate = (double) userVote / totalVote; - reward += voteRate * totalReward; + reward += (long) (voteRate * totalReward); } return reward; } diff --git a/crypto/src/main/java/org/tron/common/crypto/Rsv.java b/crypto/src/main/java/org/tron/common/crypto/Rsv.java new file mode 100644 index 00000000000..136a77a4592 --- /dev/null +++ b/crypto/src/main/java/org/tron/common/crypto/Rsv.java @@ -0,0 +1,26 @@ +package org.tron.common.crypto; + + +import java.util.Arrays; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class Rsv { + + private final byte[] r; + private final byte[] s; + private final byte v; + + + public static Rsv fromSignature(byte[] sign) { + byte[] r = Arrays.copyOfRange(sign, 0, 32); + byte[] s = Arrays.copyOfRange(sign, 32, 64); + byte v = sign[64]; + if (v < 27) { + v += (byte) 27; //revId -> v + } + return new Rsv(r, s, v); + } +} diff --git a/framework/src/main/java/org/tron/core/capsule/utils/RLP.java b/framework/src/main/java/org/tron/core/capsule/utils/RLP.java index 24b5c502a1c..e5ace11206d 100644 --- a/framework/src/main/java/org/tron/core/capsule/utils/RLP.java +++ b/framework/src/main/java/org/tron/core/capsule/utils/RLP.java @@ -165,7 +165,7 @@ static short decodeShort(byte[] data, int index) { byte pow = (byte) (length - 1); for (int i = 1; i <= length; ++i) { // << (8 * pow) == bit shift to 0 (*1), 8 (*256) , 16 (*65..) - value += (data[index + i] & 0xFF) << (8 * pow); + value += (short) ((data[index + i] & 0xFF) << (8 * pow)); pow--; } } else { diff --git a/framework/src/main/java/org/tron/core/services/http/GetBrokerageServlet.java b/framework/src/main/java/org/tron/core/services/http/GetBrokerageServlet.java index 1fbd94fe690..6ef22e198f9 100644 --- a/framework/src/main/java/org/tron/core/services/http/GetBrokerageServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/GetBrokerageServlet.java @@ -28,8 +28,7 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) { response.getWriter().println("{\"brokerage\": " + value + "}"); } catch (DecoderException | IllegalArgumentException e) { try { - response.getWriter() - .println("{\"Error\": " + "\"INVALID address, " + e.getMessage() + "\"}"); + response.getWriter().println("{\"Error\": " + "\"INVALID address\"}"); } catch (IOException ioe) { logger.debug("IOException: {}", ioe.getMessage()); } diff --git a/framework/src/main/java/org/tron/core/services/http/GetRewardServlet.java b/framework/src/main/java/org/tron/core/services/http/GetRewardServlet.java index c4d97f46c57..78042072df8 100644 --- a/framework/src/main/java/org/tron/core/services/http/GetRewardServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/GetRewardServlet.java @@ -27,8 +27,7 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) { response.getWriter().println("{\"reward\": " + value + "}"); } catch (DecoderException | IllegalArgumentException e) { try { - response.getWriter() - .println("{\"Error\": " + "\"INVALID address, " + e.getMessage() + "\"}"); + response.getWriter().println("{\"Error\": " + "\"INVALID address\"}"); } catch (IOException ioe) { logger.debug("IOException: {}", ioe.getMessage()); } diff --git a/framework/src/main/java/org/tron/core/services/http/solidity/GetTransactionByIdSolidityServlet.java b/framework/src/main/java/org/tron/core/services/http/solidity/GetTransactionByIdSolidityServlet.java index f98c7450afc..5998bc0850f 100644 --- a/framework/src/main/java/org/tron/core/services/http/solidity/GetTransactionByIdSolidityServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/solidity/GetTransactionByIdSolidityServlet.java @@ -30,12 +30,7 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) { String input = request.getParameter("value"); fillResponse(ByteString.copyFrom(ByteArray.fromHexString(input)), visible, response); } catch (Exception e) { - logger.debug("Exception: {}", e.getMessage()); - try { - response.getWriter().println(e.getMessage()); - } catch (IOException ioe) { - logger.debug("IOException: {}", ioe.getMessage()); - } + Util.processError(e, response); } } @@ -46,12 +41,7 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response) JsonFormat.merge(params.getParams(), build, params.isVisible()); fillResponse(build.build().getValue(), params.isVisible(), response); } catch (Exception e) { - logger.debug("Exception: {}", e.getMessage()); - try { - response.getWriter().println(e.getMessage()); - } catch (IOException ioe) { - logger.debug("IOException: {}", ioe.getMessage()); - } + Util.processError(e, response); } } diff --git a/framework/src/main/java/org/tron/core/services/http/solidity/GetTransactionInfoByIdSolidityServlet.java b/framework/src/main/java/org/tron/core/services/http/solidity/GetTransactionInfoByIdSolidityServlet.java index 0408215f09d..197f5aaec0d 100644 --- a/framework/src/main/java/org/tron/core/services/http/solidity/GetTransactionInfoByIdSolidityServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/solidity/GetTransactionInfoByIdSolidityServlet.java @@ -1,7 +1,6 @@ package org.tron.core.services.http.solidity; import com.google.protobuf.ByteString; -import java.io.IOException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; @@ -37,12 +36,7 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) { response.getWriter().println(JsonFormat.printToString(transInfo, visible)); } } catch (Exception e) { - logger.debug("Exception: {}", e.getMessage()); - try { - response.getWriter().println(e.getMessage()); - } catch (IOException ioe) { - logger.debug("IOException: {}", ioe.getMessage()); - } + Util.processError(e, response); } } @@ -60,12 +54,7 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response) response.getWriter().println(JsonFormat.printToString(transInfo, params.isVisible())); } } catch (Exception e) { - logger.debug("Exception: {}", e.getMessage()); - try { - response.getWriter().println(e.getMessage()); - } catch (IOException ioe) { - logger.debug("IOException: {}", ioe.getMessage()); - } + Util.processError(e, response); } } diff --git a/framework/src/main/java/org/tron/core/services/jsonrpc/types/TransactionResult.java b/framework/src/main/java/org/tron/core/services/jsonrpc/types/TransactionResult.java index 389c58505cd..57650355d46 100644 --- a/framework/src/main/java/org/tron/core/services/jsonrpc/types/TransactionResult.java +++ b/framework/src/main/java/org/tron/core/services/jsonrpc/types/TransactionResult.java @@ -5,9 +5,9 @@ import com.fasterxml.jackson.annotation.JsonPropertyOrder; import com.google.protobuf.ByteString; -import java.util.Arrays; import lombok.Getter; import lombok.ToString; +import org.tron.common.crypto.Rsv; import org.tron.common.utils.ByteArray; import org.tron.core.Wallet; import org.tron.core.capsule.BlockCapsule; @@ -65,16 +65,10 @@ private void parseSignature(Transaction tx) { } ByteString signature = tx.getSignature(0); // r[32] + s[32] + v[1] - byte[] signData = signature.toByteArray(); - byte[] rByte = Arrays.copyOfRange(signData, 0, 32); - byte[] sByte = Arrays.copyOfRange(signData, 32, 64); - byte vByte = signData[64]; - if (vByte < 27) { - vByte += 27; - } - v = ByteArray.toJsonHex(vByte); - r = ByteArray.toJsonHex(rByte); - s = ByteArray.toJsonHex(sByte); + Rsv rsv = Rsv.fromSignature(signature.toByteArray()); + r = ByteArray.toJsonHex(rsv.getR()); + s = ByteArray.toJsonHex(rsv.getS()); + v = ByteArray.toJsonHex(rsv.getV()); } private String parseInput(Transaction tx) { diff --git a/framework/src/main/java/org/tron/core/zen/address/SpendingKey.java b/framework/src/main/java/org/tron/core/zen/address/SpendingKey.java index 18f18af3742..f7f030d00e2 100644 --- a/framework/src/main/java/org/tron/core/zen/address/SpendingKey.java +++ b/framework/src/main/java/org/tron/core/zen/address/SpendingKey.java @@ -89,7 +89,7 @@ public DiversifierT defaultDiversifier() throws BadItemException, ZksnarkExcepti throw new BadItemException( "librustzcash_check_diversifier does not return valid diversifier"); } - blob[33] += 1; + blob[33] += (byte) 1; } finally { JLibsodium.freeState(state); } diff --git a/framework/src/test/java/org/tron/common/utils/client/utils/TransactionUtils.java b/framework/src/test/java/org/tron/common/utils/client/utils/TransactionUtils.java index b6226d01aae..63ffe1b58ff 100644 --- a/framework/src/test/java/org/tron/common/utils/client/utils/TransactionUtils.java +++ b/framework/src/test/java/org/tron/common/utils/client/utils/TransactionUtils.java @@ -14,6 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ +import static org.tron.core.capsule.TransactionCapsule.getBase64FromByteString; import com.google.protobuf.ByteString; import java.security.SignatureException; @@ -116,20 +117,6 @@ public static byte[] getOwner(Transaction.Contract contract) { } } - /** - * constructor. - */ - - public static String getBase64FromByteString(ByteString sign) { - byte[] r = sign.substring(0, 32).toByteArray(); - byte[] s = sign.substring(32, 64).toByteArray(); - byte v = sign.byteAt(64); - if (v < 27) { - v += 27; //revId -> v - } - ECDSASignature signature = ECDSASignature.fromComponents(r, s, v); - return signature.toBase64(); - } /* * 1. check hash From 21f98b45e4a6b9faf06502d452a45b2cefd75bac Mon Sep 17 00:00:00 2001 From: halibobo1205 Date: Mon, 30 Jun 2025 16:08:13 +0800 Subject: [PATCH 23/59] feat(db): merge related tools into FullNode/Toolkit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - `--solidity`: SolidityNode → FullNode - `--keystore`: KeystoreFactory → FullNode - `db convert`: DBConvert → Toolkit --- .../common/parameter/CommonParameter.java | 8 + framework/build.gradle | 23 - .../java/org/tron/core/config/args/Args.java | 3 +- .../main/java/org/tron/program/DBConvert.java | 413 ------------------ .../main/java/org/tron/program/FullNode.java | 10 +- .../org/tron/program/KeystoreFactory.java | 12 +- .../java/org/tron/program/SolidityNode.java | 27 +- .../org/tron/core/config/args/ArgsTest.java | 4 +- .../java/org/tron/program/DBConvertTest.java | 116 ----- .../org/tron/program/SolidityNodeTest.java | 13 +- 10 files changed, 39 insertions(+), 590 deletions(-) delete mode 100644 framework/src/main/java/org/tron/program/DBConvert.java delete mode 100644 framework/src/test/java/org/tron/program/DBConvertTest.java diff --git a/common/src/main/java/org/tron/common/parameter/CommonParameter.java b/common/src/main/java/org/tron/common/parameter/CommonParameter.java index 45893970fb0..14233863de3 100644 --- a/common/src/main/java/org/tron/common/parameter/CommonParameter.java +++ b/common/src/main/java/org/tron/common/parameter/CommonParameter.java @@ -205,7 +205,15 @@ public class CommonParameter { //If you are running a solidity node for java tron, this flag is set to true @Getter @Setter + @Parameter(names = {"--solidity"}, description = "running a solidity node for java tron") public boolean solidityNode = false; + + //If you are running KeystoreFactory, this flag is set to true + @Getter + @Setter + @Parameter(names = {"--keystore"}, description = "running KeystoreFactory") + public boolean keystore = false; + @Getter @Setter public int rpcPort; diff --git a/framework/build.gradle b/framework/build.gradle index b4c20d2da76..9763f2980e2 100644 --- a/framework/build.gradle +++ b/framework/build.gradle @@ -212,35 +212,12 @@ startScripts.enabled = false run.enabled = false tasks.distTar.enabled = false -createScript(project, 'org.tron.program.SolidityNode', 'SolidityNode') createScript(project, 'org.tron.program.FullNode', 'FullNode') -createScript(project, 'org.tron.program.KeystoreFactory', 'KeystoreFactory') -createScript(project, 'org.tron.program.DBConvert', 'DBConvert') - def releaseBinary = hasProperty('binaryRelease') ? getProperty('binaryRelease') : 'true' -def skipSolidity = hasProperty('skipSolidity') ? true : false -def skipKeystore = hasProperty('skipKeystore') ? true : false -def skipConvert = hasProperty('skipConvert') ? true : false -def skipAll = hasProperty('skipAll') ? true : false if (releaseBinary == 'true') { artifacts { archives(binaryRelease('buildFullNodeJar', 'FullNode', 'org.tron.program.FullNode')) } - if (!skipAll) { - if (!skipSolidity) { - artifacts { - archives(binaryRelease('buildSolidityNodeJar', 'SolidityNode', 'org.tron.program.SolidityNode'))} - } - if (!skipKeystore) { - artifacts { - archives(binaryRelease('buildKeystoreFactoryJar', 'KeystoreFactory', 'org.tron.program.KeystoreFactory'))} - } - if (!skipConvert) { - artifacts { - archives(binaryRelease('buildDBConvertJar', 'DBConvert', 'org.tron.program.DBConvert'))} - } - } - } task copyToParent(type: Copy) { diff --git a/framework/src/main/java/org/tron/core/config/args/Args.java b/framework/src/main/java/org/tron/core/config/args/Args.java index 5b4178e6e4c..07a44ac5ed4 100644 --- a/framework/src/main/java/org/tron/core/config/args/Args.java +++ b/framework/src/main/java/org/tron/core/config/args/Args.java @@ -172,6 +172,7 @@ public static void clearParam() { PARAMETER.tcpNettyWorkThreadNum = 0; PARAMETER.udpNettyWorkThreadNum = 0; PARAMETER.solidityNode = false; + PARAMETER.keystore = false; PARAMETER.trustNodeAddr = ""; PARAMETER.walletExtensionApi = false; PARAMETER.estimateEnergy = false; @@ -345,7 +346,7 @@ private static String getCommitIdAbbrev() { private static Map getOptionGroup() { String[] tronOption = new String[] {"version", "help", "shellConfFileName", "logbackPath", - "eventSubscribe"}; + "eventSubscribe", "solidityNode", "keystore"}; String[] dbOption = new String[] {"outputDirectory"}; String[] witnessOption = new String[] {"witness", "privateKey"}; String[] vmOption = new String[] {"debug"}; diff --git a/framework/src/main/java/org/tron/program/DBConvert.java b/framework/src/main/java/org/tron/program/DBConvert.java deleted file mode 100644 index 7b9d63544dc..00000000000 --- a/framework/src/main/java/org/tron/program/DBConvert.java +++ /dev/null @@ -1,413 +0,0 @@ -package org.tron.program; - -import static org.fusesource.leveldbjni.JniDBFactory.factory; -import static org.tron.common.math.Maths.max; - -import java.io.File; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.concurrent.ArrayBlockingQueue; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; -import lombok.extern.slf4j.Slf4j; -import org.fusesource.leveldbjni.JniDBFactory; -import org.iq80.leveldb.CompressionType; -import org.iq80.leveldb.DB; -import org.iq80.leveldb.DBIterator; -import org.rocksdb.BlockBasedTableConfig; -import org.rocksdb.BloomFilter; -import org.rocksdb.ComparatorOptions; -import org.rocksdb.Options; -import org.rocksdb.RocksDB; -import org.rocksdb.RocksDBException; -import org.rocksdb.RocksIterator; -import org.rocksdb.Status; -import org.tron.common.utils.FileUtil; -import org.tron.common.utils.MarketOrderPriceComparatorForLevelDB; -import org.tron.common.utils.MarketOrderPriceComparatorForRockDB; -import org.tron.common.utils.PropUtil; - -@Slf4j -public class DBConvert implements Callable { - - static { - RocksDB.loadLibrary(); - } - - private final String srcDir; - private final String dstDir; - private final String dbName; - private final Path srcDbPath; - private final Path dstDbPath; - - private long srcDbKeyCount = 0L; - private long dstDbKeyCount = 0L; - private long srcDbKeySum = 0L; - private long dstDbKeySum = 0L; - private long srcDbValueSum = 0L; - private long dstDbValueSum = 0L; - private final long startTime; - private static final int CPUS = Runtime.getRuntime().availableProcessors(); - private static final int BATCH = 256; - private static final String CHECKPOINT_V2_DIR_NAME = "checkpoint"; - - - @Override - public Boolean call() throws Exception { - return doConvert(); - } - - public DBConvert(String src, String dst, String name) { - this.srcDir = src; - this.dstDir = dst; - this.dbName = name; - this.srcDbPath = Paths.get(this.srcDir, name); - this.dstDbPath = Paths.get(this.dstDir, name); - this.startTime = System.currentTimeMillis(); - } - - public static org.iq80.leveldb.Options newDefaultLevelDbOptions() { - org.iq80.leveldb.Options dbOptions = new org.iq80.leveldb.Options(); - dbOptions.createIfMissing(true); - dbOptions.paranoidChecks(true); - dbOptions.verifyChecksums(true); - dbOptions.compressionType(CompressionType.SNAPPY); - dbOptions.blockSize(4 * 1024); - dbOptions.writeBufferSize(10 * 1024 * 1024); - dbOptions.cacheSize(10 * 1024 * 1024L); - dbOptions.maxOpenFiles(1000); - return dbOptions; - } - - public static void main(String[] args) { - int code = run(args); - logger.info("exit code {}.", code); - System.out.printf("exit code %d.\n", code); - System.exit(code); - } - - public static int run(String[] args) { - String dbSrc; - String dbDst; - if (args.length < 2) { - dbSrc = "output-directory/database"; - dbDst = "output-directory-dst/database"; - } else { - dbSrc = args[0]; - dbDst = args[1]; - } - File dbDirectory = new File(dbSrc); - if (!dbDirectory.exists()) { - logger.info(" {} does not exist.", dbSrc); - return 404; - } - List files = Arrays.stream(Objects.requireNonNull(dbDirectory.listFiles())) - .filter(File::isDirectory) - .filter(e -> !CHECKPOINT_V2_DIR_NAME.equals(e.getName())) - .collect(Collectors.toList()); - - // add checkpoint v2 convert - File cpV2Dir = new File(Paths.get(dbSrc, CHECKPOINT_V2_DIR_NAME).toString()); - List cpList = null; - if (cpV2Dir.exists()) { - cpList = Arrays.stream(Objects.requireNonNull(cpV2Dir.listFiles())) - .filter(File::isDirectory) - .collect(Collectors.toList()); - } - - if (files.isEmpty()) { - logger.info("{} does not contain any database.", dbSrc); - return 0; - } - final long time = System.currentTimeMillis(); - final List> res = new ArrayList<>(); - - final ThreadPoolExecutor esDb = new ThreadPoolExecutor( - CPUS, 16 * CPUS, 1, TimeUnit.MINUTES, - new ArrayBlockingQueue<>(CPUS, true), Executors.defaultThreadFactory(), - new ThreadPoolExecutor.CallerRunsPolicy()); - - esDb.allowCoreThreadTimeOut(true); - - files.forEach(f -> res.add(esDb.submit(new DBConvert(dbSrc, dbDst, f.getName())))); - // convert v2 - if (cpList != null) { - cpList.forEach(f -> res.add(esDb.submit( - new DBConvert(dbSrc + "/" + CHECKPOINT_V2_DIR_NAME, - dbDst + "/" + CHECKPOINT_V2_DIR_NAME, f.getName())))); - } - - int fails = res.size(); - - for (Future re : res) { - try { - if (re.get()) { - fails--; - } - } catch (InterruptedException e) { - logger.error("{}", e); - Thread.currentThread().interrupt(); - } catch (ExecutionException e) { - logger.error("{}", e); - } - } - - esDb.shutdown(); - logger.info("database convert use {} seconds total.", - (System.currentTimeMillis() - time) / 1000); - if (fails > 0) { - logger.error("failed!!!!!!!!!!!!!!!!!!!!!!!! size:{}", fails); - } - return fails; - } - - public DB newLevelDb(Path db) throws Exception { - DB database; - File file = db.toFile(); - org.iq80.leveldb.Options dbOptions = newDefaultLevelDbOptions(); - if ("market_pair_price_to_order".equalsIgnoreCase(this.dbName)) { - dbOptions.comparator(new MarketOrderPriceComparatorForLevelDB()); - } - database = factory.open(file, dbOptions); - return database; - } - - private Options newDefaultRocksDbOptions() { - Options options = new Options(); - options.setCreateIfMissing(true); - options.setIncreaseParallelism(1); - options.setNumLevels(7); - options.setMaxOpenFiles(5000); - options.setTargetFileSizeBase(64 * 1024 * 1024); - options.setTargetFileSizeMultiplier(1); - options.setMaxBytesForLevelBase(512 * 1024 * 1024); - options.setMaxBackgroundCompactions(max(1, Runtime.getRuntime().availableProcessors(), true)); - options.setLevel0FileNumCompactionTrigger(4); - options.setLevelCompactionDynamicLevelBytes(true); - if ("market_pair_price_to_order".equalsIgnoreCase(this.dbName)) { - options.setComparator(new MarketOrderPriceComparatorForRockDB(new ComparatorOptions())); - } - final BlockBasedTableConfig tableCfg; - options.setTableFormatConfig(tableCfg = new BlockBasedTableConfig()); - tableCfg.setBlockSize(64 * 1024); - tableCfg.setBlockCacheSize(32 * 1024 * 1024); - tableCfg.setCacheIndexAndFilterBlocks(true); - tableCfg.setPinL0FilterAndIndexBlocksInCache(true); - tableCfg.setFilter(new BloomFilter(10, false)); - options.prepareForBulkLoad(); - return options; - } - - public RocksDB newRocksDb(Path db) { - RocksDB database = null; - try (Options options = newDefaultRocksDbOptions()) { - database = RocksDB.open(options, db.toString()); - } catch (Exception e) { - logger.error("{}", e); - } - return database; - } - - private void batchInsert(RocksDB rocks, List keys, List values) - throws Exception { - try (org.rocksdb.WriteBatch batch = new org.rocksdb.WriteBatch()) { - for (int i = 0; i < keys.size(); i++) { - byte[] k = keys.get(i); - byte[] v = values.get(i); - batch.put(k, v); - } - write(rocks, batch); - } - keys.clear(); - values.clear(); - } - - /** - * https://github.com/facebook/rocksdb/issues/6625 - * @param rocks db - * @param batch write batch - * @throws Exception RocksDBException - */ - private void write(RocksDB rocks, org.rocksdb.WriteBatch batch) throws Exception { - try { - rocks.write(new org.rocksdb.WriteOptions(), batch); - } catch (RocksDBException e) { - // retry - if (maybeRetry(e)) { - TimeUnit.MILLISECONDS.sleep(1); - write(rocks, batch); - } else { - throw e; - } - } - } - - private boolean maybeRetry(RocksDBException e) { - boolean retry = false; - if (e.getStatus() != null) { - retry = e.getStatus().getCode() == Status.Code.TryAgain - || e.getStatus().getCode() == Status.Code.Busy - || e.getStatus().getCode() == Status.Code.Incomplete; - } - return retry || (e.getMessage() != null && ("Write stall".equalsIgnoreCase(e.getMessage()) - || ("Incomplete").equalsIgnoreCase(e.getMessage()))); - } - - /** - * https://github.com/facebook/rocksdb/wiki/RocksDB-FAQ . - * What's the fastest way to load data into RocksDB? - * @param level leveldb - * @param rocks rocksdb - * @return if ok - */ - public boolean convertLevelToRocksBatchIterator(DB level, RocksDB rocks) { - // convert - List keys = new ArrayList<>(BATCH); - List values = new ArrayList<>(BATCH); - try (DBIterator levelIterator = level.iterator( - new org.iq80.leveldb.ReadOptions().fillCache(false))) { - - JniDBFactory.pushMemoryPool(1024 * 1024); - levelIterator.seekToFirst(); - - while (levelIterator.hasNext()) { - Map.Entry entry = levelIterator.next(); - byte[] key = entry.getKey(); - byte[] value = entry.getValue(); - srcDbKeyCount++; - srcDbKeySum = byteArrayToIntWithOne(srcDbKeySum, key); - srcDbValueSum = byteArrayToIntWithOne(srcDbValueSum, value); - keys.add(key); - values.add(value); - if (keys.size() >= BATCH) { - try { - batchInsert(rocks, keys, values); - } catch (Exception e) { - logger.error("{}", e); - return false; - } - } - } - - if (!keys.isEmpty()) { - try { - batchInsert(rocks, keys, values); - } catch (Exception e) { - logger.error("{}", e); - return false; - } - } - // check - check(rocks); - } catch (Exception e) { - logger.error("{}", e); - return false; - } finally { - try { - level.close(); - rocks.close(); - JniDBFactory.popMemoryPool(); - } catch (Exception e1) { - logger.error("{}", e1); - } - } - return dstDbKeyCount == srcDbKeyCount && dstDbKeySum == srcDbKeySum - && dstDbValueSum == srcDbValueSum; - } - - private void check(RocksDB rocks) throws RocksDBException { - logger.info("check database {} start", this.dbName); - // manually call CompactRange() - logger.info("compact database {} start", this.dbName); - rocks.compactRange(); - logger.info("compact database {} end", this.dbName); - // check - try (org.rocksdb.ReadOptions r = new org.rocksdb.ReadOptions().setFillCache(false); - RocksIterator rocksIterator = rocks.newIterator(r)) { - for (rocksIterator.seekToFirst(); rocksIterator.isValid(); rocksIterator.next()) { - byte[] key = rocksIterator.key(); - byte[] value = rocksIterator.value(); - dstDbKeyCount++; - dstDbKeySum = byteArrayToIntWithOne(dstDbKeySum, key); - dstDbValueSum = byteArrayToIntWithOne(dstDbValueSum, value); - } - } - logger.info("check database {} end", this.dbName); - } - - public boolean createEngine(String dir) { - String enginePath = dir + File.separator + "engine.properties"; - - if (!FileUtil.createFileIfNotExists(enginePath)) { - return false; - } - - return PropUtil.writeProperty(enginePath, "ENGINE", "ROCKSDB"); - } - - public boolean checkDone(String dir) { - String enginePath = dir + File.separator + "engine.properties"; - return FileUtil.isExists(enginePath); - - } - - public boolean doConvert() throws Exception { - - if (checkDone(this.dstDbPath.toString())) { - logger.info(" {} is done, skip it.", this.dbName); - return true; - } - - File levelDbFile = srcDbPath.toFile(); - if (!levelDbFile.exists()) { - logger.info(" {} does not exist.", srcDbPath.toString()); - return false; - } - - DB level = newLevelDb(srcDbPath); - - if (this.dstDbPath.toFile().exists()) { - logger.info(" {} begin to clear exist database directory", this.dbName); - FileUtil.deleteDir(this.dstDbPath.toFile()); - logger.info(" {} clear exist database directory done.", this.dbName); - } - - FileUtil.createDirIfNotExists(dstDir); - RocksDB rocks = newRocksDb(dstDbPath); - - logger.info("Convert database {} start", this.dbName); - boolean result = convertLevelToRocksBatchIterator(level, rocks) - && createEngine(dstDbPath.toString()); - long etime = System.currentTimeMillis(); - - if (result) { - logger.info("Convert database {} successful end with {} key-value {} minutes", - this.dbName, this.srcDbKeyCount, (etime - this.startTime) / 1000.0 / 60); - } else { - logger.info("Convert database {} failure", this.dbName); - if (this.dstDbPath.toFile().exists()) { - logger.info(" {} begin to clear exist database directory", this.dbName); - FileUtil.deleteDir(this.dstDbPath.toFile()); - logger.info(" {} clear exist database directory done.", this.dbName); - } - } - return result; - } - - public long byteArrayToIntWithOne(long sum, byte[] b) { - for (byte oneByte : b) { - sum += oneByte; - } - return sum; - } -} \ No newline at end of file diff --git a/framework/src/main/java/org/tron/program/FullNode.java b/framework/src/main/java/org/tron/program/FullNode.java index bd275de544c..e17ce8c8531 100644 --- a/framework/src/main/java/org/tron/program/FullNode.java +++ b/framework/src/main/java/org/tron/program/FullNode.java @@ -21,12 +21,20 @@ public class FullNode { */ public static void main(String[] args) { ExitManager.initExceptionHandler(); - logger.info("Full node running."); Args.setParam(args, Constant.TESTNET_CONF); CommonParameter parameter = Args.getInstance(); LogService.load(parameter.getLogbackPath()); + if (parameter.isSolidityNode()) { + SolidityNode.start(); + return; + } + if (parameter.isKeystore()) { + KeystoreFactory.start(); + return; + } + logger.info("Full node running."); if (Args.getInstance().isDebug()) { logger.info("in debug mode, it won't check energy time"); } else { diff --git a/framework/src/main/java/org/tron/program/KeystoreFactory.java b/framework/src/main/java/org/tron/program/KeystoreFactory.java index bfd2df22856..8199d7e9076 100755 --- a/framework/src/main/java/org/tron/program/KeystoreFactory.java +++ b/framework/src/main/java/org/tron/program/KeystoreFactory.java @@ -1,6 +1,5 @@ package org.tron.program; -import com.beust.jcommander.JCommander; import java.io.File; import java.io.IOException; import java.util.Scanner; @@ -11,8 +10,6 @@ import org.tron.common.parameter.CommonParameter; import org.tron.common.utils.ByteArray; import org.tron.common.utils.Utils; -import org.tron.core.Constant; -import org.tron.core.config.args.Args; import org.tron.core.exception.CipherException; import org.tron.keystore.Credentials; import org.tron.keystore.WalletUtils; @@ -22,15 +19,8 @@ public class KeystoreFactory { private static final String FilePath = "Wallet"; - public static void main(String[] args) { - Args.setParam(args, Constant.TESTNET_CONF); + public static void start() { KeystoreFactory cli = new KeystoreFactory(); - - JCommander.newBuilder() - .addObject(cli) - .build() - .parse(args); - cli.run(); } diff --git a/framework/src/main/java/org/tron/program/SolidityNode.java b/framework/src/main/java/org/tron/program/SolidityNode.java index b774ab03aaa..3367141e2a5 100644 --- a/framework/src/main/java/org/tron/program/SolidityNode.java +++ b/framework/src/main/java/org/tron/program/SolidityNode.java @@ -5,21 +5,17 @@ import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.atomic.AtomicLong; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.BooleanUtils; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.util.ObjectUtils; import org.tron.common.application.Application; import org.tron.common.application.ApplicationFactory; import org.tron.common.application.TronApplicationContext; import org.tron.common.client.DatabaseGrpcClient; -import org.tron.common.exit.ExitManager; import org.tron.common.parameter.CommonParameter; import org.tron.common.prometheus.Metrics; import org.tron.core.ChainBaseManager; -import org.tron.core.Constant; import org.tron.core.capsule.BlockCapsule; import org.tron.core.config.DefaultConfig; -import org.tron.core.config.args.Args; import org.tron.core.db.Manager; import org.tron.core.exception.TronError; import org.tron.protos.Protocol.Block; @@ -55,25 +51,12 @@ public SolidityNode(Manager dbManager) { /** * Start the SolidityNode. */ - public static void main(String[] args) { - ExitManager.initExceptionHandler(); + public static void start() { logger.info("Solidity node is running."); - Args.setParam(args, Constant.TESTNET_CONF); CommonParameter parameter = CommonParameter.getInstance(); - - logger.info("index switch is {}", - BooleanUtils.toStringOnOff(BooleanUtils - .toBoolean(parameter.getStorage().getIndexSwitch()))); - if (ObjectUtils.isEmpty(parameter.getTrustNodeAddr())) { - logger.error("Trust node is not set."); - return; - } - parameter.setSolidityNode(true); - - if (parameter.isHelp()) { - logger.info("Here is the help message."); - return; + throw new TronError(new IllegalArgumentException("Trust node is not set."), + TronError.ErrCode.SOLID_NODE_INIT); } // init metrics first Metrics.init(); @@ -88,11 +71,11 @@ public static void main(String[] args) { context.registerShutdownHook(); appT.startup(); SolidityNode node = new SolidityNode(appT.getDbManager()); - node.start(); + node.run(); appT.blockUntilShutdown(); } - private void start() { + private void run() { try { new Thread(this::getBlock).start(); new Thread(this::processBlock).start(); diff --git a/framework/src/test/java/org/tron/core/config/args/ArgsTest.java b/framework/src/test/java/org/tron/core/config/args/ArgsTest.java index 4bb8e7e4909..4b656e463be 100644 --- a/framework/src/test/java/org/tron/core/config/args/ArgsTest.java +++ b/framework/src/test/java/org/tron/core/config/args/ArgsTest.java @@ -57,7 +57,7 @@ public void destroy() { @Test public void get() { - Args.setParam(new String[] {"-c", Constant.TEST_CONF}, Constant.TESTNET_CONF); + Args.setParam(new String[] {"-c", Constant.TEST_CONF, "--keystore"}, Constant.TESTNET_CONF); CommonParameter parameter = Args.getInstance(); @@ -127,6 +127,8 @@ public void get() { Assert.assertEquals(address, ByteArray.toHexString(Args.getLocalWitnesses() .getWitnessAccountAddress(CommonParameter.getInstance().isECKeyCryptoEngine()))); + + Assert.assertTrue(parameter.isKeystore()); } @Test diff --git a/framework/src/test/java/org/tron/program/DBConvertTest.java b/framework/src/test/java/org/tron/program/DBConvertTest.java deleted file mode 100644 index 7b3f797d627..00000000000 --- a/framework/src/test/java/org/tron/program/DBConvertTest.java +++ /dev/null @@ -1,116 +0,0 @@ -package org.tron.program; - -import static org.fusesource.leveldbjni.JniDBFactory.factory; - -import java.io.File; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.util.UUID; -import org.iq80.leveldb.DB; -import org.iq80.leveldb.Options; -import org.junit.AfterClass; -import org.junit.Assert; -import org.junit.BeforeClass; -import org.junit.Test; -import org.tron.common.utils.ByteArray; -import org.tron.common.utils.FileUtil; -import org.tron.common.utils.MarketOrderPriceComparatorForLevelDB; -import org.tron.core.capsule.MarketOrderIdListCapsule; -import org.tron.core.capsule.utils.MarketUtils; - -public class DBConvertTest { - - - private static final String INPUT_DIRECTORY = "output-directory/convert-database/"; - private static final String OUTPUT_DIRECTORY = "output-directory/convert-database-dest/"; - private static final String ACCOUNT = "account"; - private static final String MARKET = "market_pair_price_to_order"; - - - @BeforeClass - public static void init() throws IOException { - if (new File(INPUT_DIRECTORY).mkdirs()) { - initDB(new File(INPUT_DIRECTORY,ACCOUNT)); - initDB(new File(INPUT_DIRECTORY,MARKET)); - } - } - - private static void initDB(File file) throws IOException { - Options dbOptions = DBConvert.newDefaultLevelDbOptions(); - if (file.getName().contains("market_pair_price_to_order")) { - dbOptions.comparator(new MarketOrderPriceComparatorForLevelDB()); - try (DB db = factory.open(file,dbOptions)) { - - byte[] sellTokenID1 = ByteArray.fromString("100"); - byte[] buyTokenID1 = ByteArray.fromString("200"); - byte[] pairPriceKey1 = MarketUtils.createPairPriceKey( - sellTokenID1, - buyTokenID1, - 1000L, - 2001L - ); - byte[] pairPriceKey2 = MarketUtils.createPairPriceKey( - sellTokenID1, - buyTokenID1, - 1000L, - 2002L - ); - byte[] pairPriceKey3 = MarketUtils.createPairPriceKey( - sellTokenID1, - buyTokenID1, - 1000L, - 2003L - ); - - MarketOrderIdListCapsule capsule1 = new MarketOrderIdListCapsule(ByteArray.fromLong(1), - ByteArray.fromLong(1)); - MarketOrderIdListCapsule capsule2 = new MarketOrderIdListCapsule(ByteArray.fromLong(2), - ByteArray.fromLong(2)); - MarketOrderIdListCapsule capsule3 = new MarketOrderIdListCapsule(ByteArray.fromLong(3), - ByteArray.fromLong(3)); - - //Use out-of-order insertion,key in store should be 1,2,3 - db.put(pairPriceKey1, capsule1.getData()); - db.put(pairPriceKey2, capsule2.getData()); - db.put(pairPriceKey3, capsule3.getData()); - } - - } else { - try (DB db = factory.open(file,dbOptions)) { - for (int i = 0; i < 100; i++) { - byte[] bytes = UUID.randomUUID().toString().getBytes(StandardCharsets.UTF_8); - db.put(bytes, bytes); - } - } - } - - } - - @AfterClass - public static void destroy() { - FileUtil.deleteDir(new File(INPUT_DIRECTORY)); - FileUtil.deleteDir(new File(OUTPUT_DIRECTORY)); - } - - @Test - public void testRun() { - String[] args = new String[] { INPUT_DIRECTORY, OUTPUT_DIRECTORY }; - Assert.assertEquals(0, DBConvert.run(args)); - } - - @Test - public void testNotExist() { - String[] args = new String[] {OUTPUT_DIRECTORY + File.separator + UUID.randomUUID(), - OUTPUT_DIRECTORY}; - Assert.assertEquals(404, DBConvert.run(args)); - } - - @Test - public void testEmpty() { - File file = new File(OUTPUT_DIRECTORY + File.separator + UUID.randomUUID()); - file.mkdirs(); - file.deleteOnExit(); - String[] args = new String[] {file.toString(), OUTPUT_DIRECTORY}; - Assert.assertEquals(0, DBConvert.run(args)); - } -} diff --git a/framework/src/test/java/org/tron/program/SolidityNodeTest.java b/framework/src/test/java/org/tron/program/SolidityNodeTest.java index a95d07c0c11..cb2be8cd688 100755 --- a/framework/src/test/java/org/tron/program/SolidityNodeTest.java +++ b/framework/src/test/java/org/tron/program/SolidityNodeTest.java @@ -1,5 +1,8 @@ package org.tron.program; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; + import javax.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.junit.Assert; @@ -8,6 +11,7 @@ import org.tron.common.client.DatabaseGrpcClient; import org.tron.core.Constant; import org.tron.core.config.args.Args; +import org.tron.core.exception.TronError; import org.tron.core.services.RpcApiService; import org.tron.core.services.http.solidity.SolidityNodeHttpApiService; import org.tron.protos.Protocol.Block; @@ -22,14 +26,19 @@ public class SolidityNodeTest extends BaseTest { SolidityNodeHttpApiService solidityNodeHttpApiService; static { - Args.setParam(new String[]{"-d", dbPath()}, Constant.TEST_CONF); - Args.getInstance().setSolidityNode(true); + Args.setParam(new String[]{"-d", dbPath(), "--solidity"}, Constant.TEST_CONF); } @Test public void testSolidityArgs() { Assert.assertNotNull(Args.getInstance().getTrustNodeAddr()); Assert.assertTrue(Args.getInstance().isSolidityNode()); + String trustNodeAddr = Args.getInstance().getTrustNodeAddr(); + Args.getInstance().setTrustNodeAddr(null); + TronError thrown = assertThrows(TronError.class, + SolidityNode::start); + assertEquals(TronError.ErrCode.SOLID_NODE_INIT, thrown.getErrCode()); + Args.getInstance().setTrustNodeAddr(trustNodeAddr); } @Test From 50d6f69ef751225826792c0ce99b289d6c6ab163 Mon Sep 17 00:00:00 2001 From: halibobo1205 <82020050+halibobo1205@users.noreply.github.com> Date: Thu, 14 Aug 2025 11:35:15 +0800 Subject: [PATCH 24/59] feat(architecture): support arm64 based on JDK17 (#6327) * feat(jvm/JEP-358): enhance NullPointerException handling - update condition to check for NullPointerException instances - maintain compatibility with both pre-Java 14 and Java 14+ versions - address JEP 358 changes to NullPointerException behavior - ensure correct handling of non-empty NPE messages in Java 14+ Refs: JEP 358, JDK-8220715 * feat(jvm/JEP-223): use java.specification.version - use `java.specification.version` instead of parsing `java.version` - ensure consistent version reporting across different Java releases(jdk10+) * feat(jvm/JDK-8176425): Add radix indication in NumberFormatException message * feat(jvm/JEP-277): remove deprecated and marked for removal code * feat(jvm/JEP-286): remove lombok.var * feat(dependencies): update dependencies for jdk17 add javax.jws-api:1.1 and javax.annotation-api:1.3.2, refs:JDK-8190378, JEP 320 remove com.carrotsearch:java-sizeof:0.0.5 bump lombok from 1.18.12 to 1.18.34, refs: [Lombok Changelog](https://projectlombok.org/changelog) bump aspectjrt from 1.18.13 to 1.9.8, refs: [AspectJ Java version compatibility](https://eclipse.dev/aspectj/doc/latest/release/JavaVersionCompatibility.html) * feat(JVM): add jvm options for jdk17 * feat(docker): support ARM64 * feat(db): keep leveldb and rocksdb same behaviors for db operations * feat(db): remove rocksDB compatibility with LevelDB * feat(db): update leveldb and rocksdb for arm * feat(math): add hard-code for arm pow * test(ci):fix test failed * feat(test): add test for hashcode and web3_clientVersion * feat(db): remove leveldb support for arm64 * feat(jdk): X86 platforms strictly restrict JDK 1.8 usage --- README.md | 67 +++- .../src/main/java/org/tron/core/vm/VM.java | 5 +- .../vm/program/invoke/ProgramInvokeImpl.java | 5 +- .../org/tron/core/vm/repository/Type.java | 2 +- .../org/tron/core/vm/repository/Value.java | 2 +- build.gradle | 55 ++- chainbase/build.gradle | 2 - .../tron/common/storage/OptionsPicker.java | 15 + .../leveldb/LevelDbDataSourceImpl.java | 70 ++-- .../rocksdb/RocksDbDataSourceImpl.java | 372 +++++++----------- .../org/tron/common/utils/StorageUtils.java | 11 +- .../tron/core/capsule/utils/MarketUtils.java | 79 +--- .../java/org/tron/core/db/TronDatabase.java | 15 +- .../tron/core/db/TronStoreWithRevoking.java | 15 +- .../tron/core/db/common/DbSourceInter.java | 11 +- .../db/common/iterator/RockStoreIterator.java | 7 +- .../db/common/iterator/StoreIterator.java | 5 + .../org/tron/core/db2/common/TxCacheDB.java | 9 +- .../store/MarketPairPriceToOrderStore.java | 23 +- common/build.gradle | 33 +- .../tron/common/setting/RocksDbSettings.java | 94 ++++- .../src/main/java/org/tron/core/Constant.java | 4 + .../org/tron/core/config/args/Storage.java | 9 + .../org/tron/common/crypto/zksnark/Fp12.java | 2 +- .../org/tron/common/crypto/zksnark/Fp2.java | 2 +- .../org/tron/common/crypto/zksnark/Fp6.java | 2 +- docker/arm64/Dockerfile | 33 ++ framework/build.gradle | 16 +- .../java/org/tron/core/config/args/Args.java | 8 +- .../services/http/GetProposalByIdServlet.java | 2 +- .../services/jsonrpc/TronJsonRpcImpl.java | 7 +- framework/src/main/resources/config.conf | 10 +- .../leveldb/LevelDbDataSourceImplTest.java | 158 ++++++++ .../leveldb/RocksDbDataSourceImplTest.java | 141 ++++++- .../org/tron/common/utils/HashCodeTest.java | 23 ++ .../capsule/utils/ExchangeProcessorTest.java | 65 ++- .../org/tron/core/db2/CheckpointV2Test.java | 14 +- .../db2/RevokingDbWithCacheNewValueTest.java | 13 +- .../org/tron/core/db2/SnapshotImplTest.java | 15 +- .../tron/core/db2/SnapshotManagerTest.java | 14 +- .../org/tron/core/db2/SnapshotRootTest.java | 27 +- .../org/tron/core/jsonrpc/ApiUtilTest.java | 14 + .../tron/core/jsonrpc/JsonrpcServiceTest.java | 14 +- .../test/java/org/tron/core/net/BaseNet.java | 11 +- .../core/services/RpcApiServicesTest.java | 8 +- .../LiteFnQueryGrpcInterceptorTest.java | 9 +- .../filter/RpcApiAccessInterceptorTest.java | 8 +- .../org/tron/keystroe/CredentialsTest.java | 23 +- gradle/jdk17/java-tron.vmoptions | 8 + gradle/verification-metadata.xml | 78 +++- platform/build.gradle | 17 + .../arm/org/tron/common/math/MathWrapper.java | 254 ++++++++++++ .../MarketOrderPriceComparatorForRocksDB.java | 32 ++ .../common/org/tron/common/arch/Arch.java | 86 ++++ .../tron/common/utils/MarketComparator.java | 114 +++--- .../MarketOrderPriceComparatorForLevelDB.java | 4 +- .../org/tron/common/math/MathWrapper.java | 0 .../MarketOrderPriceComparatorForRocksDB.java | 7 +- plugins/README.md | 12 +- plugins/build.gradle | 39 +- .../arm/org/tron/plugins/ArchiveManifest.java | 29 ++ .../java/arm/org/tron/plugins/DbArchive.java | 50 +++ .../{ => common}/org/tron/plugins/Db.java | 0 .../org/tron/plugins/DbConvert.java | 46 +-- .../{ => common}/org/tron/plugins/DbCopy.java | 0 .../{ => common}/org/tron/plugins/DbLite.java | 0 .../{ => common}/org/tron/plugins/DbMove.java | 0 .../{ => common}/org/tron/plugins/DbRoot.java | 0 .../org/tron/plugins/Toolkit.java | 0 .../org/tron/plugins/utils/ByteArray.java | 0 .../org/tron/plugins/utils/CryptoUitls.java | 0 .../org/tron/plugins/utils/DBUtils.java | 12 +- .../org/tron/plugins/utils/FileUtils.java | 0 .../org/tron/plugins/utils/MarketUtils.java | 68 ++++ .../org/tron/plugins/utils/MerkleRoot.java | 0 .../org/tron/plugins/utils/Sha256Hash.java | 0 .../tron/plugins/utils/db/DBInterface.java | 0 .../org/tron/plugins/utils/db/DBIterator.java | 0 .../org/tron/plugins/utils/db/DbTool.java | 24 +- .../tron/plugins/utils/db/LevelDBImpl.java | 0 .../plugins/utils/db/LevelDBIterator.java | 0 .../tron/plugins/utils/db/RockDBIterator.java | 0 .../tron/plugins/utils/db/RocksDBImpl.java | 0 .../MarketOrderPriceComparatorForLevelDB.java | 28 -- .../MarketOrderPriceComparatorForRockDB.java | 38 -- .../org/tron/plugins/ArchiveManifest.java | 3 +- .../{ => x86}/org/tron/plugins/DbArchive.java | 3 +- .../java/org/tron/plugins/DbCopyTest.java | 13 +- .../java/org/tron/plugins/DbLiteTest.java | 10 +- .../java/org/tron/plugins/DbMoveTest.java | 79 ++-- .../java/org/tron/plugins/DbRootTest.java | 21 +- .../test/java/org/tron/plugins/DbTest.java | 33 +- .../{ => leveldb}/ArchiveManifestTest.java | 46 +-- .../plugins/{ => leveldb}/DbArchiveTest.java | 45 +-- .../plugins/{ => leveldb}/DbConvertTest.java | 16 +- .../{ => leveldb}/DbLiteLevelDbTest.java | 3 +- .../{ => leveldb}/DbLiteLevelDbV2Test.java | 3 +- .../{ => rocksdb}/DbLiteRocksDbTest.java | 3 +- .../plugins/rocksdb/DbLiteRocksDbV2Test.java | 13 + plugins/src/test/resources/config.conf | 6 - settings.gradle | 1 + start.sh.simple | 195 +++++++++ 102 files changed, 2100 insertions(+), 888 deletions(-) create mode 100644 chainbase/src/main/java/org/tron/common/storage/OptionsPicker.java create mode 100644 docker/arm64/Dockerfile create mode 100644 framework/src/test/java/org/tron/common/utils/HashCodeTest.java create mode 100644 gradle/jdk17/java-tron.vmoptions create mode 100644 platform/build.gradle create mode 100644 platform/src/main/java/arm/org/tron/common/math/MathWrapper.java create mode 100644 platform/src/main/java/arm/org/tron/common/utils/MarketOrderPriceComparatorForRocksDB.java create mode 100644 platform/src/main/java/common/org/tron/common/arch/Arch.java rename plugins/src/main/java/org/tron/plugins/utils/MarketUtils.java => platform/src/main/java/common/org/tron/common/utils/MarketComparator.java (50%) rename {chainbase/src/main/java => platform/src/main/java/common}/org/tron/common/utils/MarketOrderPriceComparatorForLevelDB.java (87%) rename {common/src/main/java => platform/src/main/java/x86}/org/tron/common/math/MathWrapper.java (100%) rename chainbase/src/main/java/org/tron/common/utils/MarketOrderPriceComparatorForRockDB.java => platform/src/main/java/x86/org/tron/common/utils/MarketOrderPriceComparatorForRocksDB.java (70%) create mode 100644 plugins/src/main/java/arm/org/tron/plugins/ArchiveManifest.java create mode 100644 plugins/src/main/java/arm/org/tron/plugins/DbArchive.java rename plugins/src/main/java/{ => common}/org/tron/plugins/Db.java (100%) rename plugins/src/main/java/{ => common}/org/tron/plugins/DbConvert.java (91%) rename plugins/src/main/java/{ => common}/org/tron/plugins/DbCopy.java (100%) rename plugins/src/main/java/{ => common}/org/tron/plugins/DbLite.java (100%) rename plugins/src/main/java/{ => common}/org/tron/plugins/DbMove.java (100%) rename plugins/src/main/java/{ => common}/org/tron/plugins/DbRoot.java (100%) rename plugins/src/main/java/{ => common}/org/tron/plugins/Toolkit.java (100%) rename plugins/src/main/java/{ => common}/org/tron/plugins/utils/ByteArray.java (100%) rename plugins/src/main/java/{ => common}/org/tron/plugins/utils/CryptoUitls.java (100%) rename plugins/src/main/java/{ => common}/org/tron/plugins/utils/DBUtils.java (94%) rename plugins/src/main/java/{ => common}/org/tron/plugins/utils/FileUtils.java (100%) create mode 100644 plugins/src/main/java/common/org/tron/plugins/utils/MarketUtils.java rename plugins/src/main/java/{ => common}/org/tron/plugins/utils/MerkleRoot.java (100%) rename plugins/src/main/java/{ => common}/org/tron/plugins/utils/Sha256Hash.java (100%) rename plugins/src/main/java/{ => common}/org/tron/plugins/utils/db/DBInterface.java (100%) rename plugins/src/main/java/{ => common}/org/tron/plugins/utils/db/DBIterator.java (100%) rename plugins/src/main/java/{ => common}/org/tron/plugins/utils/db/DbTool.java (85%) rename plugins/src/main/java/{ => common}/org/tron/plugins/utils/db/LevelDBImpl.java (100%) rename plugins/src/main/java/{ => common}/org/tron/plugins/utils/db/LevelDBIterator.java (100%) rename plugins/src/main/java/{ => common}/org/tron/plugins/utils/db/RockDBIterator.java (100%) rename plugins/src/main/java/{ => common}/org/tron/plugins/utils/db/RocksDBImpl.java (100%) delete mode 100644 plugins/src/main/java/org/tron/plugins/comparator/MarketOrderPriceComparatorForLevelDB.java delete mode 100644 plugins/src/main/java/org/tron/plugins/comparator/MarketOrderPriceComparatorForRockDB.java rename plugins/src/main/java/{ => x86}/org/tron/plugins/ArchiveManifest.java (98%) rename plugins/src/main/java/{ => x86}/org/tron/plugins/DbArchive.java (98%) rename plugins/src/test/java/org/tron/plugins/{ => leveldb}/ArchiveManifestTest.java (69%) rename plugins/src/test/java/org/tron/plugins/{ => leveldb}/DbArchiveTest.java (71%) rename plugins/src/test/java/org/tron/plugins/{ => leveldb}/DbConvertTest.java (76%) rename plugins/src/test/java/org/tron/plugins/{ => leveldb}/DbLiteLevelDbTest.java (76%) rename plugins/src/test/java/org/tron/plugins/{ => leveldb}/DbLiteLevelDbV2Test.java (76%) rename plugins/src/test/java/org/tron/plugins/{ => rocksdb}/DbLiteRocksDbTest.java (76%) create mode 100644 plugins/src/test/java/org/tron/plugins/rocksdb/DbLiteRocksDbV2Test.java create mode 100644 start.sh.simple diff --git a/README.md b/README.md index fab0f63d303..3a21f3d63f4 100644 --- a/README.md +++ b/README.md @@ -59,8 +59,20 @@ TRON enables large-scale development and engagement. With over 2000 transactions # Building the Source Code -Building java-tron requires `git` package and 64-bit version of `Oracle JDK 1.8` to be installed, other JDK versions are not supported yet. Make sure you operate on `Linux` and `MacOS` operating systems. +Building java-tron requires `git` package +## Operating systems +Make sure you operate on `Linux` or `MacOS` operating systems, other operating systems are not supported yet. + +## Architecture + +### x86_64 +64-bit version of `Oracle JDK 1.8` to be installed, other JDK versions are not supported yet. + +### ARM64 +64-bit version of `JDK 17` to be installed, other JDK versions are not supported yet. + +### build Clone the repo and switch to the `master` branch ```bash @@ -77,8 +89,19 @@ $ ./gradlew clean build -x test # Running java-tron -Running java-tron requires 64-bit version of `Oracle JDK 1.8` to be installed, other JDK versions are not supported yet. Make sure you operate on `Linux` and `MacOS` operating systems. +## Operating systems +Make sure you operate on `Linux` or `MacOS` operating systems, other operating systems are not supported yet. + +## Architecture + +### X86_64 +Requires 64-bit version of `Oracle JDK 1.8` to be installed, other JDK versions are not supported yet. +### ARM64 +Requires 64-bit version of `JDK 17` to be installed, other JDK versions are not supported yet. + + +## Configuration flile Get the mainnet configuration file: [main_net_config.conf](https://github.com/tronprotocol/tron-deployment/blob/master/main_net_config.conf), other network configuration files can be found [here](https://github.com/tronprotocol/tron-deployment). ## Hardware Requirements @@ -100,6 +123,7 @@ Recommended: Full node has full historical data, it is the entry point into the TRON network, it can be used by other processes as a gateway into the TRON network via HTTP and GRPC endpoints. You can interact with the TRON network through full node:transfer assets, deploy contracts, interact with contracts and so on. `-c` parameter specifies a configuration file to run a full node: +### x86_64 JDK 1.8 ```bash $ nohup java -Xms9G -Xmx9G -XX:ReservedCodeCacheSize=256m \ -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m \ @@ -111,6 +135,19 @@ $ nohup java -Xms9G -Xmx9G -XX:ReservedCodeCacheSize=256m \ -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=70 \ -jar FullNode.jar -c main_net_config.conf >> start.log 2>&1 & ``` +### ARM64 JDK 17 +```bash +$ nohup java -Xms9G -Xmx9G -XX:+UseZGC \ + -Xlog:gc*:file=gc.log:time,uptime,level,tags:filecount=50,filesize=100M \ + -XX:ReservedCodeCacheSize=256m \ + -XX:+UseCodeCacheFlushing \ + -XX:MetaspaceSize=256m \ + -XX:MaxMetaspaceSize=512m \ + -XX:MaxDirectMemorySize=1g \ + -XX:+HeapDumpOnOutOfMemoryError \ + -jar FullNode.jar -c main_net_config.conf >> start.log 2>&1 & +``` + ## Running a super representative node for mainnet @@ -126,6 +163,7 @@ Fill in the private key of a super representative address into the `localwitness then run the following command to start the node: +### x86_64 JDK 1.8 ```bash $ nohup java -Xms9G -Xmx9G -XX:ReservedCodeCacheSize=256m \ -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m \ @@ -137,9 +175,22 @@ $ nohup java -Xms9G -Xmx9G -XX:ReservedCodeCacheSize=256m \ -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=70 \ -jar FullNode.jar --witness -c main_net_config.conf >> start.log 2>&1 & ``` +### ARM64 JDK 17 +```bash +$ nohup java -Xms9G -Xmx9G -XX:+UseZGC \ + -Xlog:gc*:file=gc.log:time,uptime,level,tags:filecount=50,filesize=100M \ + -XX:ReservedCodeCacheSize=256m \ + -XX:+UseCodeCacheFlushing \ + -XX:MetaspaceSize=256m \ + -XX:MaxMetaspaceSize=512m \ + -XX:MaxDirectMemorySize=1g \ + -XX:+HeapDumpOnOutOfMemoryError \ + -jar FullNode.jar --witness -c main_net_config.conf >> start.log 2>&1 & +``` ## Quick Start Tool +### x86_64 JDK 1.8 An easier way to build and run java-tron is to use `start.sh`. `start.sh` is a quick start script written in the Shell language. You can use it to build and run java-tron quickly and easily. Here are some common use cases of the scripting tool @@ -150,6 +201,18 @@ Here are some common use cases of the scripting tool For more details, please refer to the tool [guide](./shell.md). +### ARM64 JDK 17 +You can refer to the [start.sh.simple](start.sh.simple). + +```bash +# cp start.sh.simple start.sh +# Usage: +# sh start.sh # Start the java-tron FullNode +# sh start.sh -s # Stop the java-tron FullNode +# sh start.sh [options] # Start with additional java-tron options,such as: -c config.conf -d /path_to_data, etc. +# +``` + ## Run inside Docker container One of the quickest ways to get `java-tron` up and running on your machine is by using Docker: diff --git a/actuator/src/main/java/org/tron/core/vm/VM.java b/actuator/src/main/java/org/tron/core/vm/VM.java index 2150df04c64..b1d7b027601 100644 --- a/actuator/src/main/java/org/tron/core/vm/VM.java +++ b/actuator/src/main/java/org/tron/core/vm/VM.java @@ -108,7 +108,10 @@ public static void play(Program program, JumpTable jumpTable) { } catch (JVMStackOverFlowException | OutOfTimeException e) { throw e; } catch (RuntimeException e) { - if (StringUtils.isEmpty(e.getMessage())) { + // https://openjdk.org/jeps/358 + // https://bugs.openjdk.org/browse/JDK-8220715 + // since jdk 14, the NullPointerExceptions message is not empty + if (e instanceof NullPointerException || StringUtils.isEmpty(e.getMessage())) { logger.warn("Unknown Exception occurred, tx id: {}", Hex.toHexString(program.getRootTransactionId()), e); program.setRuntimeFailure(new RuntimeException("Unknown Exception")); diff --git a/actuator/src/main/java/org/tron/core/vm/program/invoke/ProgramInvokeImpl.java b/actuator/src/main/java/org/tron/core/vm/program/invoke/ProgramInvokeImpl.java index 7997aaedcd5..ede20103609 100644 --- a/actuator/src/main/java/org/tron/core/vm/program/invoke/ProgramInvokeImpl.java +++ b/actuator/src/main/java/org/tron/core/vm/program/invoke/ProgramInvokeImpl.java @@ -314,7 +314,7 @@ public boolean equals(Object o) { @Override public int hashCode() { - return new Integer(Boolean.valueOf(byTestingSuite).hashCode() + return Boolean.valueOf(byTestingSuite).hashCode() + Boolean.valueOf(byTransaction).hashCode() + address.hashCode() + balance.hashCode() @@ -326,8 +326,7 @@ public int hashCode() { + origin.hashCode() + prevHash.hashCode() + deposit.hashCode() - + timestamp.hashCode() - ).hashCode(); + + timestamp.hashCode(); } @Override diff --git a/actuator/src/main/java/org/tron/core/vm/repository/Type.java b/actuator/src/main/java/org/tron/core/vm/repository/Type.java index e0842e6a593..9dfbd69f9ae 100644 --- a/actuator/src/main/java/org/tron/core/vm/repository/Type.java +++ b/actuator/src/main/java/org/tron/core/vm/repository/Type.java @@ -73,7 +73,7 @@ public boolean equals(Object obj) { @Override public int hashCode() { - return new Integer(type).hashCode(); + return type; } @Override diff --git a/actuator/src/main/java/org/tron/core/vm/repository/Value.java b/actuator/src/main/java/org/tron/core/vm/repository/Value.java index bf5d99c9c94..1df758f0b3e 100644 --- a/actuator/src/main/java/org/tron/core/vm/repository/Value.java +++ b/actuator/src/main/java/org/tron/core/vm/repository/Value.java @@ -58,6 +58,6 @@ public boolean equals(Object obj) { @Override public int hashCode() { - return new Integer(type.hashCode() + Objects.hashCode(value)).hashCode(); + return type.hashCode() + Objects.hashCode(value); } } diff --git a/build.gradle b/build.gradle index e2d90c55db0..e66ee616807 100644 --- a/build.gradle +++ b/build.gradle @@ -1,3 +1,4 @@ +import org.gradle.nativeplatform.platform.internal.Architectures allprojects { version = "1.0.0" apply plugin: "java-library" @@ -5,13 +6,47 @@ allprojects { springVersion = "5.3.39" } } +def arch = System.getProperty("os.arch").toLowerCase() +def javaVersion = JavaVersion.current() +def isArm64 = Architectures.AARCH64.isAlias(arch) +def archSource = isArm64 ? "arm" : "x86" + +ext.archInfo = [ + name : arch, + java : javaVersion, + isArm64 : isArm64, + sourceSets: [ + main: [ + java: [ + srcDirs: ["src/main/java/common", "src/main/java/${archSource}"] + ] + ], + test: [ + java: [ + srcDirs: ["src/test/java"] + ] + ] + ], + requires: [ + JavaVersion: isArm64 ? JavaVersion.VERSION_17 : JavaVersion.VERSION_1_8, + RocksdbVersion: isArm64 ? '7.7.3' : '5.15.10' + ], + VMOptions: isArm64 ? "${rootDir}/gradle/jdk17/java-tron.vmoptions" : "${rootDir}/gradle/java-tron.vmoptions" +] + +if (!archInfo.java.is(archInfo.requires.JavaVersion)) { + throw new GradleException("Java ${archInfo.requires.JavaVersion} is required for ${archInfo.name}. Detected version ${archInfo.java}") +} + +println "Building for architecture: ${archInfo.name}, Java version: ${archInfo.java}" + subprojects { apply plugin: "jacoco" apply plugin: "maven-publish" sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.current() [compileJava, compileTestJava]*.options*.encoding = 'UTF-8' jacoco { @@ -51,10 +86,16 @@ subprojects { implementation group: 'joda-time', name: 'joda-time', version: '2.3' implementation group: 'org.bouncycastle', name: 'bcprov-jdk15on', version: '1.69' - compileOnly 'org.projectlombok:lombok:1.18.12' - annotationProcessor 'org.projectlombok:lombok:1.18.12' - testCompileOnly 'org.projectlombok:lombok:1.18.12' - testAnnotationProcessor 'org.projectlombok:lombok:1.18.12' + compileOnly 'org.projectlombok:lombok:1.18.34' + annotationProcessor 'org.projectlombok:lombok:1.18.34' + testCompileOnly 'org.projectlombok:lombok:1.18.34' + testAnnotationProcessor 'org.projectlombok:lombok:1.18.34' + + // https://www.oracle.com/java/technologies/javase/11-relnote-issues.html#JDK-8190378 + implementation group: 'javax.annotation', name: 'javax.annotation-api', version: '1.3.2' + // for json-rpc, see https://github.com/briandilley/jsonrpc4j/issues/278 + implementation group: 'javax.jws', name: 'javax.jws-api', version: '1.1' + annotationProcessor group: 'javax.annotation', name: 'javax.annotation-api', version: '1.3.2' testImplementation group: 'junit', name: 'junit', version: '4.13.2' testImplementation "org.mockito:mockito-core:4.11.0" @@ -73,6 +114,10 @@ subprojects { reproducibleFileOrder = true duplicatesStrategy = DuplicatesStrategy.INCLUDE // allow duplicates } + tasks.withType(Test).configureEach { + // https://docs.gradle.org/current/dsl/org.gradle.api.tasks.testing.Test.html#org.gradle.api.tasks.testing.Test:environment + environment 'CI', 'true' + } } task copyToParent(type: Copy) { diff --git a/chainbase/build.gradle b/chainbase/build.gradle index 187ce9d32d3..1a07ff95fa5 100644 --- a/chainbase/build.gradle +++ b/chainbase/build.gradle @@ -10,8 +10,6 @@ dependencies { api project(":common") api project(":crypto") api "org.fusesource.jansi:jansi:$jansiVersion" - api group: 'commons-io', name: 'commons-io', version: '2.18.0' - api 'io.github.tronprotocol:zksnark-java-sdk:1.0.0' exclude(group: 'commons-io', module: 'commons-io') api 'org.reflections:reflections:0.9.11' } diff --git a/chainbase/src/main/java/org/tron/common/storage/OptionsPicker.java b/chainbase/src/main/java/org/tron/common/storage/OptionsPicker.java new file mode 100644 index 00000000000..acace12b39d --- /dev/null +++ b/chainbase/src/main/java/org/tron/common/storage/OptionsPicker.java @@ -0,0 +1,15 @@ +package org.tron.common.storage; + +import org.tron.common.setting.RocksDbSettings; +import org.tron.common.utils.StorageUtils; + +public class OptionsPicker { + + protected org.iq80.leveldb.Options getOptionsByDbNameForLevelDB(String dbName) { + return StorageUtils.getOptionsByDbName(dbName); + } + + protected org.rocksdb.Options getOptionsByDbNameForRocksDB(String dbName) { + return RocksDbSettings.getOptionsByDbName(dbName); + } +} diff --git a/chainbase/src/main/java/org/tron/common/storage/leveldb/LevelDbDataSourceImpl.java b/chainbase/src/main/java/org/tron/common/storage/leveldb/LevelDbDataSourceImpl.java index 506ecdcb6c7..72bdd80d64e 100644 --- a/chainbase/src/main/java/org/tron/common/storage/leveldb/LevelDbDataSourceImpl.java +++ b/chainbase/src/main/java/org/tron/common/storage/leveldb/LevelDbDataSourceImpl.java @@ -17,7 +17,10 @@ import static org.fusesource.leveldbjni.JniDBFactory.factory; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Strings; import com.google.common.collect.Sets; +import com.google.common.primitives.Bytes; import java.io.File; import java.io.IOException; import java.nio.file.Files; @@ -30,18 +33,14 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; -import java.util.Objects; import java.util.Set; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.stream.Collectors; import java.util.stream.Stream; import java.util.stream.StreamSupport; - -import com.google.common.primitives.Bytes; import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.iq80.leveldb.CompressionType; import org.iq80.leveldb.DB; import org.iq80.leveldb.DBIterator; import org.iq80.leveldb.Logger; @@ -54,6 +53,7 @@ import org.tron.common.storage.WriteOptionsWrapper; import org.tron.common.storage.metric.DbStat; import org.tron.common.utils.FileUtil; +import org.tron.common.utils.PropUtil; import org.tron.common.utils.StorageUtils; import org.tron.core.db.common.DbSourceInter; import org.tron.core.db.common.iterator.StoreIterator; @@ -75,6 +75,7 @@ public class LevelDbDataSourceImpl extends DbStat implements DbSourceInter allKeys() { resetDbLock.readLock().lock(); @@ -243,6 +272,7 @@ public Set allKeys() { } @Deprecated + @VisibleForTesting @Override public Set allValues() { resetDbLock.readLock().lock(); @@ -362,6 +392,8 @@ public Map prefixQuery(byte[] key) { } } + @Deprecated + @VisibleForTesting @Override public long getTotal() throws RuntimeException { resetDbLock.readLock().lock(); @@ -378,13 +410,6 @@ public long getTotal() throws RuntimeException { } } - private void updateByBatchInner(Map rows) throws Exception { - try (WriteBatch batch = database.createWriteBatch()) { - innerBatchUpdate(rows,batch); - database.write(batch, writeOptions); - } - } - private void updateByBatchInner(Map rows, WriteOptions options) throws Exception { try (WriteBatch batch = database.createWriteBatch()) { innerBatchUpdate(rows,batch); @@ -404,30 +429,23 @@ private void innerBatchUpdate(Map rows, WriteBatch batch) { @Override public void updateByBatch(Map rows, WriteOptionsWrapper options) { - resetDbLock.readLock().lock(); - try { - updateByBatchInner(rows, options.level); - } catch (Exception e) { - try { - updateByBatchInner(rows, options.level); - } catch (Exception e1) { - throw new RuntimeException(e); - } - } finally { - resetDbLock.readLock().unlock(); - } + this.updateByBatch(rows, options.level); } @Override public void updateByBatch(Map rows) { + this.updateByBatch(rows, writeOptions); + } + + private void updateByBatch(Map rows, WriteOptions options) { resetDbLock.readLock().lock(); try { - updateByBatchInner(rows); + updateByBatchInner(rows, options); } catch (Exception e) { try { - updateByBatchInner(rows); + updateByBatchInner(rows, options); } catch (Exception e1) { - throw new RuntimeException(e); + throw new RuntimeException(e1); } } finally { resetDbLock.readLock().unlock(); diff --git a/chainbase/src/main/java/org/tron/common/storage/rocksdb/RocksDbDataSourceImpl.java b/chainbase/src/main/java/org/tron/common/storage/rocksdb/RocksDbDataSourceImpl.java index 5c051bdc101..af34dff670c 100644 --- a/chainbase/src/main/java/org/tron/common/storage/rocksdb/RocksDbDataSourceImpl.java +++ b/chainbase/src/main/java/org/tron/common/storage/rocksdb/RocksDbDataSourceImpl.java @@ -1,5 +1,7 @@ package org.tron.common.storage.rocksdb; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Strings; import com.google.common.collect.Sets; import com.google.common.primitives.Bytes; import java.io.File; @@ -20,10 +22,7 @@ import java.util.stream.Collectors; import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.rocksdb.BlockBasedTableConfig; -import org.rocksdb.BloomFilter; import org.rocksdb.Checkpoint; -import org.rocksdb.DirectComparator; import org.rocksdb.InfoLogLevel; import org.rocksdb.Logger; import org.rocksdb.Options; @@ -31,11 +30,11 @@ import org.rocksdb.RocksDB; import org.rocksdb.RocksDBException; import org.rocksdb.RocksIterator; -import org.rocksdb.Statistics; import org.rocksdb.Status; import org.rocksdb.WriteBatch; import org.rocksdb.WriteOptions; import org.slf4j.LoggerFactory; +import org.tron.common.error.TronDBException; import org.tron.common.setting.RocksDbSettings; import org.tron.common.storage.WriteOptionsWrapper; import org.tron.common.storage.metric.DbStat; @@ -53,36 +52,28 @@ public class RocksDbDataSourceImpl extends DbStat implements DbSourceInter, Iterable>, Instance { - ReadOptions readOpts; private String dataBaseName; private RocksDB database; + private Options options; private volatile boolean alive; private String parentPath; private ReadWriteLock resetDbLock = new ReentrantReadWriteLock(); private static final String KEY_ENGINE = "ENGINE"; private static final String ROCKSDB = "ROCKSDB"; - private DirectComparator comparator; private static final org.slf4j.Logger rocksDbLogger = LoggerFactory.getLogger(ROCKSDB); - public RocksDbDataSourceImpl(String parentPath, String name, RocksDbSettings settings, - DirectComparator comparator) { + public RocksDbDataSourceImpl(String parentPath, String name, Options options) { this.dataBaseName = name; this.parentPath = parentPath; - this.comparator = comparator; - RocksDbSettings.setRocksDbSettings(settings); - initDB(); - } - - public RocksDbDataSourceImpl(String parentPath, String name, RocksDbSettings settings) { - this.dataBaseName = name; - this.parentPath = parentPath; - RocksDbSettings.setRocksDbSettings(settings); + this.options = options; initDB(); } + @VisibleForTesting public RocksDbDataSourceImpl(String parentPath, String name) { this.parentPath = parentPath; this.dataBaseName = name; + this.options = RocksDbSettings.getOptionsByDbName(name); } public Path getDbPath() { @@ -125,40 +116,65 @@ public void resetDb() { } } - private boolean quitIfNotAlive() { + private void throwIfNotAlive() { if (!isAlive()) { - logger.warn("DB {} is not alive.", dataBaseName); + throw new TronDBException("DB " + this.getDBName() + " is closed."); } - return !isAlive(); } + /** copy from {@link org.fusesource.leveldbjni.internal#checkArgNotNull} */ + private static void checkArgNotNull(Object value, String name) { + if (value == null) { + throw new IllegalArgumentException("The " + name + " argument cannot be null"); + } + } + + @Deprecated + @VisibleForTesting @Override public Set allKeys() throws RuntimeException { resetDbLock.readLock().lock(); - try { - if (quitIfNotAlive()) { - return null; - } + try (final RocksIterator iter = getRocksIterator()) { Set result = Sets.newHashSet(); - try (final RocksIterator iter = getRocksIterator()) { - for (iter.seekToFirst(); iter.isValid(); iter.next()) { - result.add(iter.key()); - } - return result; + for (iter.seekToFirst(); iter.isValid(); iter.next()) { + result.add(iter.key()); } + return result; } finally { resetDbLock.readLock().unlock(); } } + @Deprecated + @VisibleForTesting @Override public Set allValues() throws RuntimeException { - return null; + resetDbLock.readLock().lock(); + try (final RocksIterator iter = getRocksIterator()) { + Set result = Sets.newHashSet(); + for (iter.seekToFirst(); iter.isValid(); iter.next()) { + result.add(iter.value()); + } + return result; + } finally { + resetDbLock.readLock().unlock(); + } } + @Deprecated + @VisibleForTesting @Override public long getTotal() throws RuntimeException { - return 0; + resetDbLock.readLock().lock(); + try (final RocksIterator iter = getRocksIterator()) { + long total = 0; + for (iter.seekToFirst(); iter.isValid(); iter.next()) { + total++; + } + return total; + } finally { + resetDbLock.readLock().unlock(); + } } @Override @@ -168,12 +184,18 @@ public String getDBName() { @Override public void setDBName(String name) { + this.dataBaseName = name; } public boolean checkOrInitEngine() { String dir = getDbPath().toString(); String enginePath = dir + File.separator + "engine.properties"; - + File currentFile = new File(dir, "CURRENT"); + if (currentFile.exists() && !Paths.get(enginePath).toFile().exists()) { + // if the CURRENT file exists, but the engine.properties file does not exist, it is LevelDB + logger.error(" You are trying to open a LevelDB database with RocksDB engine."); + return false; + } if (FileUtil.createDirIfNotExists(dir)) { if (!FileUtil.createFileIfNotExists(enginePath)) { return false; @@ -184,7 +206,7 @@ public boolean checkOrInitEngine() { // for the first init engine String engine = PropUtil.readProperty(enginePath, KEY_ENGINE); - if (engine.isEmpty() && !PropUtil.writeProperty(enginePath, KEY_ENGINE, ROCKSDB)) { + if (Strings.isNullOrEmpty(engine) && !PropUtil.writeProperty(enginePath, KEY_ENGINE, ROCKSDB)) { return false; } engine = PropUtil.readProperty(enginePath, KEY_ENGINE); @@ -194,13 +216,10 @@ public boolean checkOrInitEngine() { public void initDB() { if (!checkOrInitEngine()) { - throw new RuntimeException( - String.format("failed to check database: %s, engine do not match", dataBaseName)); + throw new RuntimeException(String.format( + "Cannot open LevelDB database '%s' with RocksDB engine." + + " Set db.engine=LEVELDB or use RocksDB database. ", dataBaseName)); } - initDB(RocksDbSettings.getSettings()); - } - - public void initDB(RocksDbSettings settings) { resetDbLock.writeLock().lock(); try { if (isAlive()) { @@ -209,81 +228,40 @@ public void initDB(RocksDbSettings settings) { if (dataBaseName == null) { throw new IllegalArgumentException("No name set to the dbStore"); } + options.setLogger(new Logger(options) { + @Override + protected void log(InfoLogLevel infoLogLevel, String logMsg) { + rocksDbLogger.info("{} {}", dataBaseName, logMsg); + } + }); - try (Options options = new Options()) { - - // most of these options are suggested by https://github.com/facebook/rocksdb/wiki/Set-Up-Options + try { + logger.debug("Opening database {}.", dataBaseName); + final Path dbPath = getDbPath(); - // general options - if (settings.isEnableStatistics()) { - options.setStatistics(new Statistics()); - options.setStatsDumpPeriodSec(60); - } - options.setCreateIfMissing(true); - options.setIncreaseParallelism(1); - options.setLevelCompactionDynamicLevelBytes(true); - options.setMaxOpenFiles(settings.getMaxOpenFiles()); - - // general options supported user config - options.setNumLevels(settings.getLevelNumber()); - options.setMaxBytesForLevelMultiplier(settings.getMaxBytesForLevelMultiplier()); - options.setMaxBytesForLevelBase(settings.getMaxBytesForLevelBase()); - options.setMaxBackgroundCompactions(settings.getCompactThreads()); - options.setLevel0FileNumCompactionTrigger(settings.getLevel0FileNumCompactionTrigger()); - options.setTargetFileSizeMultiplier(settings.getTargetFileSizeMultiplier()); - options.setTargetFileSizeBase(settings.getTargetFileSizeBase()); - if (comparator != null) { - options.setComparator(comparator); + if (!Files.isSymbolicLink(dbPath.getParent())) { + Files.createDirectories(dbPath.getParent()); } - options.setLogger(new Logger(options) { - @Override - protected void log(InfoLogLevel infoLogLevel, String logMsg) { - rocksDbLogger.info("{} {}", dataBaseName, logMsg); - } - }); - - // table options - final BlockBasedTableConfig tableCfg; - options.setTableFormatConfig(tableCfg = new BlockBasedTableConfig()); - tableCfg.setBlockSize(settings.getBlockSize()); - tableCfg.setBlockCache(RocksDbSettings.getCache()); - tableCfg.setCacheIndexAndFilterBlocks(true); - tableCfg.setPinL0FilterAndIndexBlocksInCache(true); - tableCfg.setFilter(new BloomFilter(10, false)); - - // read options - readOpts = new ReadOptions(); - readOpts = readOpts.setPrefixSameAsStart(true) - .setVerifyChecksums(false); try { - logger.debug("Opening database {}.", dataBaseName); - final Path dbPath = getDbPath(); - - if (!Files.isSymbolicLink(dbPath.getParent())) { - Files.createDirectories(dbPath.getParent()); - } - - try { - database = RocksDB.open(options, dbPath.toString()); - } catch (RocksDBException e) { - if (Objects.equals(e.getStatus().getCode(), Status.Code.Corruption)) { - logger.error("Database {} corrupted, please delete database directory({}) " + - "and restart.", dataBaseName, parentPath, e); - } else { - logger.error("Open Database {} failed", dataBaseName, e); - } - throw new TronError(e, TronError.ErrCode.ROCKSDB_INIT); + database = RocksDB.open(options, dbPath.toString()); + } catch (RocksDBException e) { + if (Objects.equals(e.getStatus().getCode(), Status.Code.Corruption)) { + logger.error("Database {} corrupted, please delete database directory({}) " + + "and restart.", dataBaseName, parentPath, e); + } else { + logger.error("Open Database {} failed", dataBaseName, e); } - - alive = true; - } catch (IOException ioe) { - throw new RuntimeException( - String.format("failed to init database: %s", dataBaseName), ioe); + throw new TronError(e, TronError.ErrCode.ROCKSDB_INIT); } - logger.debug("Init DB {} done.", dataBaseName); + alive = true; + } catch (IOException ioe) { + throw new RuntimeException( + String.format("failed to init database: %s", dataBaseName), ioe); } + + logger.debug("Init DB {} done.", dataBaseName); } finally { resetDbLock.writeLock().unlock(); } @@ -293,9 +271,9 @@ protected void log(InfoLogLevel infoLogLevel, String logMsg) { public void putData(byte[] key, byte[] value) { resetDbLock.readLock().lock(); try { - if (quitIfNotAlive()) { - return; - } + throwIfNotAlive(); + checkArgNotNull(key, "key"); + checkArgNotNull(value, "value"); database.put(key, value); } catch (RocksDBException e) { throw new RuntimeException(dataBaseName, e); @@ -308,9 +286,8 @@ public void putData(byte[] key, byte[] value) { public byte[] getData(byte[] key) { resetDbLock.readLock().lock(); try { - if (quitIfNotAlive()) { - return null; - } + throwIfNotAlive(); + checkArgNotNull(key, "key"); return database.get(key); } catch (RocksDBException e) { throw new RuntimeException(dataBaseName, e); @@ -323,9 +300,8 @@ public byte[] getData(byte[] key) { public void deleteData(byte[] key) { resetDbLock.readLock().lock(); try { - if (quitIfNotAlive()) { - return; - } + throwIfNotAlive(); + checkArgNotNull(key, "key"); database.delete(key); } catch (RocksDBException e) { throw new RuntimeException(dataBaseName, e); @@ -344,69 +320,39 @@ public org.tron.core.db.common.iterator.DBIterator iterator() { return new RockStoreIterator(getRocksIterator()); } - private void updateByBatchInner(Map rows) throws Exception { - if (quitIfNotAlive()) { - return; - } - try (WriteBatch batch = new WriteBatch()) { - for (Map.Entry entry : rows.entrySet()) { - if (entry.getValue() == null) { - batch.delete(entry.getKey()); - } else { - batch.put(entry.getKey(), entry.getValue()); - } - } - database.write(new WriteOptions(), batch); - } - } - private void updateByBatchInner(Map rows, WriteOptions options) throws Exception { - if (quitIfNotAlive()) { - return; - } try (WriteBatch batch = new WriteBatch()) { for (Map.Entry entry : rows.entrySet()) { + checkArgNotNull(entry.getKey(), "key"); if (entry.getValue() == null) { batch.delete(entry.getKey()); } else { batch.put(entry.getKey(), entry.getValue()); } } + throwIfNotAlive(); database.write(options, batch); } } @Override public void updateByBatch(Map rows, WriteOptionsWrapper optionsWrapper) { - resetDbLock.readLock().lock(); - try { - if (quitIfNotAlive()) { - return; - } - updateByBatchInner(rows, optionsWrapper.rocks); - } catch (Exception e) { - try { - updateByBatchInner(rows); - } catch (Exception e1) { - throw new RuntimeException(dataBaseName, e1); - } - } finally { - resetDbLock.readLock().unlock(); - } + this.updateByBatch(rows, optionsWrapper.rocks); } @Override public void updateByBatch(Map rows) { + this.updateByBatch(rows, new WriteOptions()); + } + + private void updateByBatch(Map rows, WriteOptions options) { resetDbLock.readLock().lock(); try { - if (quitIfNotAlive()) { - return; - } - updateByBatchInner(rows); + updateByBatchInner(rows, options); } catch (Exception e) { try { - updateByBatchInner(rows); + updateByBatchInner(rows, options); } catch (Exception e1) { throw new RuntimeException(dataBaseName, e1); } @@ -416,45 +362,34 @@ public void updateByBatch(Map rows) { } public List getKeysNext(byte[] key, long limit) { + if (limit <= 0) { + return new ArrayList<>(); + } resetDbLock.readLock().lock(); - try { - if (quitIfNotAlive()) { - return new ArrayList<>(); - } - if (limit <= 0) { - return new ArrayList<>(); - } - - try (RocksIterator iter = getRocksIterator()) { - List result = new ArrayList<>(); - long i = 0; - for (iter.seek(key); iter.isValid() && i < limit; iter.next(), i++) { - result.add(iter.key()); - } - return result; + try (RocksIterator iter = getRocksIterator()) { + List result = new ArrayList<>(); + long i = 0; + for (iter.seek(key); iter.isValid() && i < limit; iter.next(), i++) { + result.add(iter.key()); } + return result; } finally { resetDbLock.readLock().unlock(); } } public Map getNext(byte[] key, long limit) { + if (limit <= 0) { + return Collections.emptyMap(); + } resetDbLock.readLock().lock(); - try { - if (quitIfNotAlive()) { - return null; - } - if (limit <= 0) { - return Collections.emptyMap(); - } - try (RocksIterator iter = getRocksIterator()) { - Map result = new HashMap<>(); - long i = 0; - for (iter.seek(key); iter.isValid() && i < limit; iter.next(), i++) { - result.put(iter.key(), iter.value()); - } - return result; + try (RocksIterator iter = getRocksIterator()) { + Map result = new HashMap<>(); + long i = 0; + for (iter.seek(key); iter.isValid() && i < limit; iter.next(), i++) { + result.put(iter.key(), iter.value()); } + return result; } finally { resetDbLock.readLock().unlock(); } @@ -463,78 +398,64 @@ public Map getNext(byte[] key, long limit) { @Override public Map prefixQuery(byte[] key) { resetDbLock.readLock().lock(); - try { - if (quitIfNotAlive()) { - return null; - } - try (RocksIterator iterator = getRocksIterator()) { - Map result = new HashMap<>(); - for (iterator.seek(key); iterator.isValid(); iterator.next()) { - if (Bytes.indexOf(iterator.key(), key) == 0) { - result.put(WrappedByteArray.of(iterator.key()), iterator.value()); - } else { - return result; - } + try (RocksIterator iterator = getRocksIterator()) { + Map result = new HashMap<>(); + for (iterator.seek(key); iterator.isValid(); iterator.next()) { + if (Bytes.indexOf(iterator.key(), key) == 0) { + result.put(WrappedByteArray.of(iterator.key()), iterator.value()); + } else { + return result; } - return result; } + return result; } finally { resetDbLock.readLock().unlock(); } } public Set getlatestValues(long limit) { + if (limit <= 0) { + return Sets.newHashSet(); + } resetDbLock.readLock().lock(); - try { - if (quitIfNotAlive()) { - return null; - } - if (limit <= 0) { - return Sets.newHashSet(); - } - try (RocksIterator iter = getRocksIterator()) { - Set result = Sets.newHashSet(); - long i = 0; - for (iter.seekToLast(); iter.isValid() && i < limit; iter.prev(), i++) { - result.add(iter.value()); - } - return result; + try (RocksIterator iter = getRocksIterator()) { + Set result = Sets.newHashSet(); + long i = 0; + for (iter.seekToLast(); iter.isValid() && i < limit; iter.prev(), i++) { + result.add(iter.value()); } + return result; } finally { resetDbLock.readLock().unlock(); } } - public Set getValuesNext(byte[] key, long limit) { + if (limit <= 0) { + return Sets.newHashSet(); + } resetDbLock.readLock().lock(); - try { - if (quitIfNotAlive()) { - return null; - } - if (limit <= 0) { - return Sets.newHashSet(); - } - try (RocksIterator iter = getRocksIterator()) { - Set result = Sets.newHashSet(); - long i = 0; - for (iter.seek(key); iter.isValid() && i < limit; iter.next(), i++) { - result.add(iter.value()); - } - return result; + try (RocksIterator iter = getRocksIterator()) { + Set result = Sets.newHashSet(); + long i = 0; + for (iter.seek(key); iter.isValid() && i < limit; iter.next(), i++) { + result.add(iter.value()); } + return result; } finally { resetDbLock.readLock().unlock(); } } public void backup(String dir) throws RocksDBException { + throwIfNotAlive(); Checkpoint cp = Checkpoint.create(database); cp.createCheckpoint(dir + this.getDBName()); } private RocksIterator getRocksIterator() { try ( ReadOptions readOptions = new ReadOptions().setFillCache(false)) { + throwIfNotAlive(); return database.newIterator(readOptions); } } @@ -545,7 +466,8 @@ public boolean deleteDbBakPath(String dir) { @Override public RocksDbDataSourceImpl newInstance() { - return new RocksDbDataSourceImpl(parentPath, dataBaseName, RocksDbSettings.getSettings()); + return new RocksDbDataSourceImpl(parentPath, dataBaseName, + this.options); } diff --git a/chainbase/src/main/java/org/tron/common/utils/StorageUtils.java b/chainbase/src/main/java/org/tron/common/utils/StorageUtils.java index 16df43f1534..375f0ba92fb 100644 --- a/chainbase/src/main/java/org/tron/common/utils/StorageUtils.java +++ b/chainbase/src/main/java/org/tron/common/utils/StorageUtils.java @@ -6,6 +6,7 @@ import org.apache.commons.lang3.StringUtils; import org.iq80.leveldb.Options; import org.tron.common.parameter.CommonParameter; +import org.tron.core.Constant; public class StorageUtils { @@ -52,9 +53,15 @@ public static String getOutputDirectory() { } public static Options getOptionsByDbName(String dbName) { + Options options; if (hasProperty(dbName)) { - return getProperty(dbName).getDbOptions(); + options = getProperty(dbName).getDbOptions(); + } else { + options = CommonParameter.getInstance().getStorage().newDefaultDbOptions(dbName); } - return CommonParameter.getInstance().getStorage().newDefaultDbOptions(dbName); + if (Constant.MARKET_PAIR_PRICE_TO_ORDER.equals(dbName)) { + options.comparator(new MarketOrderPriceComparatorForLevelDB()); + } + return options; } } diff --git a/chainbase/src/main/java/org/tron/core/capsule/utils/MarketUtils.java b/chainbase/src/main/java/org/tron/core/capsule/utils/MarketUtils.java index d711ac0d63b..e98bba9db08 100644 --- a/chainbase/src/main/java/org/tron/core/capsule/utils/MarketUtils.java +++ b/chainbase/src/main/java/org/tron/core/capsule/utils/MarketUtils.java @@ -25,6 +25,7 @@ import org.tron.common.crypto.Hash; import org.tron.common.utils.ByteArray; import org.tron.common.utils.ByteUtil; +import org.tron.common.utils.MarketComparator; import org.tron.core.capsule.AccountCapsule; import org.tron.core.capsule.MarketAccountOrderCapsule; import org.tron.core.capsule.MarketOrderCapsule; @@ -227,29 +228,6 @@ public static byte[] createPairKey(byte[] sellTokenId, byte[] buyTokenId) { return result; } - /** - * Note: the params should be the same token pair, or you should change the order. - * All the quantity should be bigger than 0. - * */ - public static int comparePrice(long price1SellQuantity, long price1BuyQuantity, - long price2SellQuantity, long price2BuyQuantity) { - try { - return Long.compare(multiplyExact(price1BuyQuantity, price2SellQuantity, true), - multiplyExact(price2BuyQuantity, price1SellQuantity, true)); - - } catch (ArithmeticException ex) { - // do nothing here, because we will use BigInteger to compute again - } - - BigInteger price1BuyQuantityBI = BigInteger.valueOf(price1BuyQuantity); - BigInteger price1SellQuantityBI = BigInteger.valueOf(price1SellQuantity); - BigInteger price2BuyQuantityBI = BigInteger.valueOf(price2BuyQuantity); - BigInteger price2SellQuantityBI = BigInteger.valueOf(price2SellQuantity); - - return price1BuyQuantityBI.multiply(price2SellQuantityBI) - .compareTo(price2BuyQuantityBI.multiply(price1SellQuantityBI)); - } - /** * if takerPrice >= makerPrice, return True * note: here are two different token pairs @@ -265,7 +243,8 @@ public static boolean priceMatch(MarketPrice takerPrice, MarketPrice makerPrice) // ==> Price_TRX * sellQuantity_taker/buyQuantity_taker >= Price_TRX * buyQuantity_maker/sellQuantity_maker // ==> sellQuantity_taker * sellQuantity_maker > buyQuantity_taker * buyQuantity_maker - return comparePrice(takerPrice.getBuyTokenQuantity(), takerPrice.getSellTokenQuantity(), + return MarketComparator.comparePrice(takerPrice.getBuyTokenQuantity(), + takerPrice.getSellTokenQuantity(), makerPrice.getSellTokenQuantity(), makerPrice.getBuyTokenQuantity()) >= 0; } @@ -316,57 +295,7 @@ public static void returnSellTokenRemain(MarketOrderCapsule orderCapsule, } public static int comparePriceKey(byte[] o1, byte[] o2) { - //compare pair - byte[] pair1 = new byte[TOKEN_ID_LENGTH * 2]; - byte[] pair2 = new byte[TOKEN_ID_LENGTH * 2]; - - System.arraycopy(o1, 0, pair1, 0, TOKEN_ID_LENGTH * 2); - System.arraycopy(o2, 0, pair2, 0, TOKEN_ID_LENGTH * 2); - - int pairResult = org.bouncycastle.util.Arrays.compareUnsigned(pair1, pair2); - if (pairResult != 0) { - return pairResult; - } - - //compare price - byte[] getSellTokenQuantity1 = new byte[8]; - byte[] getBuyTokenQuantity1 = new byte[8]; - - byte[] getSellTokenQuantity2 = new byte[8]; - byte[] getBuyTokenQuantity2 = new byte[8]; - - int longByteNum = 8; - - System.arraycopy(o1, TOKEN_ID_LENGTH + TOKEN_ID_LENGTH, - getSellTokenQuantity1, 0, longByteNum); - System.arraycopy(o1, TOKEN_ID_LENGTH + TOKEN_ID_LENGTH + longByteNum, - getBuyTokenQuantity1, 0, longByteNum); - - System.arraycopy(o2, TOKEN_ID_LENGTH + TOKEN_ID_LENGTH, - getSellTokenQuantity2, 0, longByteNum); - System.arraycopy(o2, TOKEN_ID_LENGTH + TOKEN_ID_LENGTH + longByteNum, - getBuyTokenQuantity2, 0, longByteNum); - - long sellTokenQuantity1 = ByteArray.toLong(getSellTokenQuantity1); - long buyTokenQuantity1 = ByteArray.toLong(getBuyTokenQuantity1); - long sellTokenQuantity2 = ByteArray.toLong(getSellTokenQuantity2); - long buyTokenQuantity2 = ByteArray.toLong(getBuyTokenQuantity2); - - if ((sellTokenQuantity1 == 0 || buyTokenQuantity1 == 0) - && (sellTokenQuantity2 == 0 || buyTokenQuantity2 == 0)) { - return 0; - } - - if (sellTokenQuantity1 == 0 || buyTokenQuantity1 == 0) { - return -1; - } - - if (sellTokenQuantity2 == 0 || buyTokenQuantity2 == 0) { - return 1; - } - - return comparePrice(sellTokenQuantity1, buyTokenQuantity1, - sellTokenQuantity2, buyTokenQuantity2); + return MarketComparator.comparePriceKey(o1, o2); } diff --git a/chainbase/src/main/java/org/tron/core/db/TronDatabase.java b/chainbase/src/main/java/org/tron/core/db/TronDatabase.java index d791e189eb4..8630fbdcdff 100644 --- a/chainbase/src/main/java/org/tron/core/db/TronDatabase.java +++ b/chainbase/src/main/java/org/tron/core/db/TronDatabase.java @@ -9,9 +9,9 @@ import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.iq80.leveldb.WriteOptions; -import org.rocksdb.DirectComparator; import org.springframework.beans.factory.annotation.Autowired; import org.tron.common.parameter.CommonParameter; +import org.tron.common.storage.OptionsPicker; import org.tron.common.storage.WriteOptionsWrapper; import org.tron.common.storage.leveldb.LevelDbDataSourceImpl; import org.tron.common.storage.metric.DbStatService; @@ -24,7 +24,7 @@ import org.tron.core.exception.ItemNotFoundException; @Slf4j(topic = "DB") -public abstract class TronDatabase implements ITronChainBase { +public abstract class TronDatabase extends OptionsPicker implements ITronChainBase { protected DbSourceInter dbSource; @Getter @@ -51,8 +51,7 @@ protected TronDatabase(String dbName) { String parentName = Paths.get(StorageUtils.getOutputDirectoryByDbName(dbName), CommonParameter.getInstance().getStorage().getDbDirectory()).toString(); dbSource = - new RocksDbDataSourceImpl(parentName, dbName, CommonParameter.getInstance() - .getRocksDBCustomSettings(), getDirectComparator()); + new RocksDbDataSourceImpl(parentName, dbName, getOptionsByDbNameForRocksDB(dbName)); } dbSource.initDB(); @@ -66,14 +65,6 @@ protected void init() { protected TronDatabase() { } - protected org.iq80.leveldb.Options getOptionsByDbNameForLevelDB(String dbName) { - return StorageUtils.getOptionsByDbName(dbName); - } - - protected DirectComparator getDirectComparator() { - return null; - } - public DbSourceInter getDbSource() { return dbSource; } diff --git a/chainbase/src/main/java/org/tron/core/db/TronStoreWithRevoking.java b/chainbase/src/main/java/org/tron/core/db/TronStoreWithRevoking.java index 4b75ddee3a4..4952b70478d 100644 --- a/chainbase/src/main/java/org/tron/core/db/TronStoreWithRevoking.java +++ b/chainbase/src/main/java/org/tron/core/db/TronStoreWithRevoking.java @@ -16,9 +16,9 @@ import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.iq80.leveldb.WriteOptions; -import org.rocksdb.DirectComparator; import org.springframework.beans.factory.annotation.Autowired; import org.tron.common.parameter.CommonParameter; +import org.tron.common.storage.OptionsPicker; import org.tron.common.storage.leveldb.LevelDbDataSourceImpl; import org.tron.common.storage.metric.DbStatService; import org.tron.common.storage.rocksdb.RocksDbDataSourceImpl; @@ -38,7 +38,7 @@ @Slf4j(topic = "DB") -public abstract class TronStoreWithRevoking implements ITronChainBase { +public abstract class TronStoreWithRevoking extends OptionsPicker implements ITronChainBase { @Getter // only for unit test protected IRevokingDB revokingDB; @@ -69,22 +69,13 @@ protected TronStoreWithRevoking(String dbName) { .getInstance().getStorage().getDbDirectory()).toString(); this.db = new RocksDB( new RocksDbDataSourceImpl(parentPath, - dbName, CommonParameter.getInstance() - .getRocksDBCustomSettings(), getDirectComparator())); + dbName, getOptionsByDbNameForRocksDB(dbName))); } else { throw new RuntimeException(String.format("db engine %s is error", dbEngine)); } this.revokingDB = new Chainbase(new SnapshotRoot(this.db)); } - protected org.iq80.leveldb.Options getOptionsByDbNameForLevelDB(String dbName) { - return StorageUtils.getOptionsByDbName(dbName); - } - - protected DirectComparator getDirectComparator() { - return null; - } - protected TronStoreWithRevoking(DB db) { this.db = db; this.revokingDB = new Chainbase(new SnapshotRoot(db)); diff --git a/chainbase/src/main/java/org/tron/core/db/common/DbSourceInter.java b/chainbase/src/main/java/org/tron/core/db/common/DbSourceInter.java index 0823b7a7cf4..18461860fc2 100755 --- a/chainbase/src/main/java/org/tron/core/db/common/DbSourceInter.java +++ b/chainbase/src/main/java/org/tron/core/db/common/DbSourceInter.java @@ -17,11 +17,10 @@ */ package org.tron.core.db.common; -import org.tron.core.db2.common.WrappedByteArray; - +import com.google.common.annotations.VisibleForTesting; import java.util.Map; import java.util.Set; - +import org.tron.core.db2.common.WrappedByteArray; public interface DbSourceInter extends BatchSourceInter, Iterable> { @@ -38,10 +37,16 @@ public interface DbSourceInter extends BatchSourceInter, void resetDb(); + @VisibleForTesting + @Deprecated Set allKeys() throws RuntimeException; + @VisibleForTesting + @Deprecated Set allValues() throws RuntimeException; + @VisibleForTesting + @Deprecated long getTotal() throws RuntimeException; void stat(); diff --git a/chainbase/src/main/java/org/tron/core/db/common/iterator/RockStoreIterator.java b/chainbase/src/main/java/org/tron/core/db/common/iterator/RockStoreIterator.java index 541f71348af..105d845dc64 100644 --- a/chainbase/src/main/java/org/tron/core/db/common/iterator/RockStoreIterator.java +++ b/chainbase/src/main/java/org/tron/core/db/common/iterator/RockStoreIterator.java @@ -47,7 +47,7 @@ public boolean hasNext() { try { close(); } catch (Exception e1) { - logger.error(e.getMessage(), e); + logger.error(e1.getMessage(), e1); } } return hasNext; @@ -79,6 +79,11 @@ public byte[] setValue(byte[] value) { }; } + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + @Override public void seek(byte[] key) { checkState(); diff --git a/chainbase/src/main/java/org/tron/core/db/common/iterator/StoreIterator.java b/chainbase/src/main/java/org/tron/core/db/common/iterator/StoreIterator.java index d771716a7e8..c2803f99637 100755 --- a/chainbase/src/main/java/org/tron/core/db/common/iterator/StoreIterator.java +++ b/chainbase/src/main/java/org/tron/core/db/common/iterator/StoreIterator.java @@ -46,6 +46,11 @@ public boolean hasNext() { } } catch (Exception e) { logger.error(e.getMessage(), e); + try { + close(); + } catch (Exception e1) { + logger.error(e1.getMessage(), e1); + } } return hasNext; diff --git a/chainbase/src/main/java/org/tron/core/db2/common/TxCacheDB.java b/chainbase/src/main/java/org/tron/core/db2/common/TxCacheDB.java index 9d2409685c9..db9516d6203 100644 --- a/chainbase/src/main/java/org/tron/core/db2/common/TxCacheDB.java +++ b/chainbase/src/main/java/org/tron/core/db2/common/TxCacheDB.java @@ -35,6 +35,7 @@ import org.tron.common.parameter.CommonParameter; import org.tron.common.prometheus.MetricKeys; import org.tron.common.prometheus.Metrics; +import org.tron.common.storage.OptionsPicker; import org.tron.common.storage.leveldb.LevelDbDataSourceImpl; import org.tron.common.storage.rocksdb.RocksDbDataSourceImpl; import org.tron.common.utils.ByteArray; @@ -48,7 +49,7 @@ import org.tron.core.store.DynamicPropertiesStore; @Slf4j(topic = "DB") -public class TxCacheDB implements DB, Flusher { +public class TxCacheDB extends OptionsPicker implements DB, Flusher { // > 65_536(= 2^16) blocks, that is the number of the reference block private static final long MAX_BLOCK_SIZE = 65536; @@ -106,7 +107,7 @@ public TxCacheDB(String name, RecentTransactionStore recentTransactionStore, if ("LEVELDB".equals(dbEngine.toUpperCase())) { this.persistentStore = new LevelDB( new LevelDbDataSourceImpl(StorageUtils.getOutputDirectoryByDbName(name), - name, StorageUtils.getOptionsByDbName(name), + name, getOptionsByDbNameForLevelDB(name), new WriteOptions().sync(CommonParameter.getInstance() .getStorage().isDbSync()))); } else if ("ROCKSDB".equals(dbEngine.toUpperCase())) { @@ -115,9 +116,7 @@ public TxCacheDB(String name, RecentTransactionStore recentTransactionStore, .getInstance().getStorage().getDbDirectory()).toString(); this.persistentStore = new RocksDB( - new RocksDbDataSourceImpl(parentPath, - name, CommonParameter.getInstance() - .getRocksDBCustomSettings())); + new RocksDbDataSourceImpl(parentPath, name, getOptionsByDbNameForRocksDB(name))); } else { throw new RuntimeException(String.format("db type: %s is not supported", dbEngine)); } diff --git a/chainbase/src/main/java/org/tron/core/store/MarketPairPriceToOrderStore.java b/chainbase/src/main/java/org/tron/core/store/MarketPairPriceToOrderStore.java index 605952328ed..391fb4249c8 100644 --- a/chainbase/src/main/java/org/tron/core/store/MarketPairPriceToOrderStore.java +++ b/chainbase/src/main/java/org/tron/core/store/MarketPairPriceToOrderStore.java @@ -3,16 +3,11 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -import org.iq80.leveldb.Options; -import org.rocksdb.ComparatorOptions; -import org.rocksdb.DirectComparator; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import org.tron.common.utils.ByteUtil; -import org.tron.common.utils.MarketOrderPriceComparatorForLevelDB; -import org.tron.common.utils.MarketOrderPriceComparatorForRockDB; -import org.tron.common.utils.StorageUtils; +import org.tron.core.Constant; import org.tron.core.capsule.MarketOrderIdListCapsule; import org.tron.core.capsule.utils.MarketUtils; import org.tron.core.db.TronStoreWithRevoking; @@ -22,24 +17,10 @@ public class MarketPairPriceToOrderStore extends TronStoreWithRevoking { @Autowired - protected MarketPairPriceToOrderStore(@Value("market_pair_price_to_order") String dbName) { + protected MarketPairPriceToOrderStore(@Value(Constant.MARKET_PAIR_PRICE_TO_ORDER) String dbName) { super(dbName); } - @Override - protected Options getOptionsByDbNameForLevelDB(String dbName) { - Options options = StorageUtils.getOptionsByDbName(dbName); - options.comparator(new MarketOrderPriceComparatorForLevelDB()); - return options; - } - - //todo: to test later - @Override - protected DirectComparator getDirectComparator() { - ComparatorOptions comparatorOptions = new ComparatorOptions(); - return new MarketOrderPriceComparatorForRockDB(comparatorOptions); - } - @Override public MarketOrderIdListCapsule get(byte[] key) throws ItemNotFoundException { byte[] value = revokingDB.get(key); diff --git a/common/build.gradle b/common/build.gradle index 8a1c87c2e46..83fe638ae50 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -6,28 +6,6 @@ version '1.0.0' sourceCompatibility = 1.8 -// Dependency versions -// --------------------------------------- -def leveldbVersion = "1.8" -// -------------------------------------- - -static def isWindows() { - return org.gradle.internal.os.OperatingSystem.current().isWindows() -} - -if (isWindows()) { - ext { - leveldbGroup = "org.ethereum" - leveldbName = "leveldbjni-all" - leveldbVersion = "1.18.3" - } -} else { - ext { - leveldbGroup = "org.fusesource.leveldbjni" - leveldbName = "leveldbjni-all" - leveldbVersion = "1.8" - } -} dependencies { api group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.18.3' // https://github.com/FasterXML/jackson-databind/issues/3627 @@ -36,14 +14,14 @@ dependencies { api group: 'commons-codec', name: 'commons-codec', version: '1.11' api group: 'com.beust', name: 'jcommander', version: '1.78' api group: 'com.typesafe', name: 'config', version: '1.3.2' - api group: leveldbGroup, name: leveldbName, version: leveldbVersion - api group: 'org.rocksdb', name: 'rocksdbjni', version: '5.15.10' api group: 'io.prometheus', name: 'simpleclient', version: '0.15.0' api group: 'io.prometheus', name: 'simpleclient_httpserver', version: '0.15.0' api group: 'io.prometheus', name: 'simpleclient_hotspot', version: '0.15.0' - api 'org.aspectj:aspectjrt:1.8.13' - api 'org.aspectj:aspectjweaver:1.8.13' - api 'org.aspectj:aspectjtools:1.8.13' + // https://openjdk.org/jeps/396, JEP 396: Strongly Encapsulate JDK Internals by Default + // https://eclipse.dev/aspectj/doc/latest/release/JavaVersionCompatibility.html + api 'org.aspectj:aspectjrt:1.9.8' + api 'org.aspectj:aspectjweaver:1.9.8' + api 'org.aspectj:aspectjtools:1.9.8' api group: 'io.github.tronprotocol', name: 'libp2p', version: '2.2.6',{ exclude group: 'io.grpc', module: 'grpc-context' exclude group: 'io.grpc', module: 'grpc-core' @@ -62,6 +40,7 @@ dependencies { exclude group: 'org.bouncycastle', module: 'bcutil-jdk18on' } api project(":protocol") + api project(":platform") } jacocoTestReport { diff --git a/common/src/main/java/org/tron/common/setting/RocksDbSettings.java b/common/src/main/java/org/tron/common/setting/RocksDbSettings.java index 7436150cae2..5c2e5eacddf 100644 --- a/common/src/main/java/org/tron/common/setting/RocksDbSettings.java +++ b/common/src/main/java/org/tron/common/setting/RocksDbSettings.java @@ -1,16 +1,21 @@ package org.tron.common.setting; +import java.util.Arrays; import lombok.Getter; -import lombok.Setter; import lombok.extern.slf4j.Slf4j; +import org.rocksdb.BlockBasedTableConfig; +import org.rocksdb.BloomFilter; +import org.rocksdb.ComparatorOptions; import org.rocksdb.LRUCache; +import org.rocksdb.Options; import org.rocksdb.RocksDB; +import org.rocksdb.Statistics; +import org.tron.common.utils.MarketOrderPriceComparatorForRocksDB; +import org.tron.core.Constant; @Slf4j public class RocksDbSettings { - @Setter - @Getter private static RocksDbSettings rocksDbSettings; @Getter @@ -40,6 +45,15 @@ public class RocksDbSettings { private static final LRUCache cache = new LRUCache(1 * 1024 * 1024 * 1024L); + private static final String[] CI_ENVIRONMENT_VARIABLES = { + "CI", + "JENKINS_URL", + "TRAVIS", + "CIRCLECI", + "GITHUB_ACTIONS", + "GITLAB_CI" + }; + private RocksDbSettings() { } @@ -60,9 +74,9 @@ public static RocksDbSettings initCustomSettings(int levelNumber, int compactThr int blockSize, long maxBytesForLevelBase, double maxBytesForLevelMultiplier, int level0FileNumCompactionTrigger, long targetFileSizeBase, - int targetFileSizeMultiplier) { + int targetFileSizeMultiplier, int maxOpenFiles) { rocksDbSettings = new RocksDbSettings() - .withMaxOpenFiles(5000) + .withMaxOpenFiles(maxOpenFiles) .withEnableStatistics(false) .withLevelNumber(levelNumber) .withCompactThreads(compactThreads) @@ -76,16 +90,17 @@ public static RocksDbSettings initCustomSettings(int levelNumber, int compactThr } public static void loggingSettings() { - logger.info(String.format( - "level number: %d, CompactThreads: %d, Blocksize: %d, maxBytesForLevelBase: %d," - + " withMaxBytesForLevelMultiplier: %f, level0FileNumCompactionTrigger: %d, " - + "withTargetFileSizeBase: %d, withTargetFileSizeMultiplier: %d", + logger.info( + "level number: {}, CompactThreads: {}, Blocksize:{}, maxBytesForLevelBase: {}," + + " withMaxBytesForLevelMultiplier: {}, level0FileNumCompactionTrigger: {}, " + + "withTargetFileSizeBase: {}, withTargetFileSizeMultiplier: {}, maxOpenFiles: {}", rocksDbSettings.getLevelNumber(), rocksDbSettings.getCompactThreads(), rocksDbSettings.getBlockSize(), rocksDbSettings.getMaxBytesForLevelBase(), rocksDbSettings.getMaxBytesForLevelMultiplier(), rocksDbSettings.getLevel0FileNumCompactionTrigger(), - rocksDbSettings.getTargetFileSizeBase(), rocksDbSettings.getTargetFileSizeMultiplier())); + rocksDbSettings.getTargetFileSizeBase(), rocksDbSettings.getTargetFileSizeMultiplier(), + rocksDbSettings.getMaxOpenFiles()); } public RocksDbSettings withMaxOpenFiles(int maxOpenFiles) { @@ -140,4 +155,63 @@ public RocksDbSettings withTargetFileSizeMultiplier(int targetFileSizeMultiplier public static LRUCache getCache() { return cache; } + + public static Options getOptionsByDbName(String dbName) { + RocksDbSettings settings = getSettings(); + + Options options = new Options(); + + // most of these options are suggested by https://github.com/facebook/rocksdb/wiki/Set-Up-Options + + // general options + if (settings.isEnableStatistics()) { + options.setStatistics(new Statistics()); + options.setStatsDumpPeriodSec(60); + } + options.setCreateIfMissing(true); + options.setIncreaseParallelism(1); + options.setLevelCompactionDynamicLevelBytes(true); + options.setMaxOpenFiles(settings.getMaxOpenFiles()); + + // general options supported user config + options.setNumLevels(settings.getLevelNumber()); + options.setMaxBytesForLevelMultiplier(settings.getMaxBytesForLevelMultiplier()); + options.setMaxBytesForLevelBase(settings.getMaxBytesForLevelBase()); + options.setMaxBackgroundCompactions(settings.getCompactThreads()); + options.setLevel0FileNumCompactionTrigger(settings.getLevel0FileNumCompactionTrigger()); + options.setTargetFileSizeMultiplier(settings.getTargetFileSizeMultiplier()); + options.setTargetFileSizeBase(settings.getTargetFileSizeBase()); + + // table options + final BlockBasedTableConfig tableCfg; + options.setTableFormatConfig(tableCfg = new BlockBasedTableConfig()); + tableCfg.setBlockSize(settings.getBlockSize()); + tableCfg.setBlockCache(RocksDbSettings.getCache()); + tableCfg.setCacheIndexAndFilterBlocks(true); + tableCfg.setPinL0FilterAndIndexBlocksInCache(true); + tableCfg.setFilter(new BloomFilter(10, false)); + if (Constant.MARKET_PAIR_PRICE_TO_ORDER.equals(dbName)) { + ComparatorOptions comparatorOptions = new ComparatorOptions(); + options.setComparator(new MarketOrderPriceComparatorForRocksDB(comparatorOptions)); + } + + if (isRunningInCI()) { + // Disable fallocate calls to avoid issues with disk space + options.setAllowFAllocate(false); + // Set WAL size limits to avoid excessive disk + options.setMaxTotalWalSize(2 * 1024 * 1024); + // Set recycle log file + options.setRecycleLogFileNum(1); + // Enable creation of missing column families + options.setCreateMissingColumnFamilies(true); + // Set max background flushes to 1 to reduce resource usage + options.setMaxBackgroundFlushes(1); + } + + return options; + } + + private static boolean isRunningInCI() { + return Arrays.stream(CI_ENVIRONMENT_VARIABLES).anyMatch(System.getenv()::containsKey); + } } diff --git a/common/src/main/java/org/tron/core/Constant.java b/common/src/main/java/org/tron/core/Constant.java index 2a6104fe6e6..c5e0ddc0ecc 100644 --- a/common/src/main/java/org/tron/core/Constant.java +++ b/common/src/main/java/org/tron/core/Constant.java @@ -24,6 +24,10 @@ public class Constant { public static final int NODE_TYPE_FULL_NODE = 0; public static final int NODE_TYPE_LIGHT_NODE = 1; + // DB NAME + public static final String MARKET_PAIR_PRICE_TO_ORDER = "market_pair_price_to_order"; + // DB NAME + // config for transaction public static final long TRANSACTION_MAX_BYTE_SIZE = 500 * 1_024L; public static final int CREATE_ACCOUNT_TRANSACTION_MIN_BYTE_SIZE = 500; diff --git a/common/src/main/java/org/tron/core/config/args/Storage.java b/common/src/main/java/org/tron/core/config/args/Storage.java index 9cf6eb6bab1..655b6b779fe 100644 --- a/common/src/main/java/org/tron/core/config/args/Storage.java +++ b/common/src/main/java/org/tron/core/config/args/Storage.java @@ -25,9 +25,11 @@ import java.util.stream.Collectors; import lombok.Getter; import lombok.Setter; +import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.iq80.leveldb.CompressionType; import org.iq80.leveldb.Options; +import org.tron.common.arch.Arch; import org.tron.common.cache.CacheStrategies; import org.tron.common.cache.CacheType; import org.tron.common.utils.DbOptionalsUtils; @@ -42,6 +44,7 @@ * @version 1.0 * @since 2018/5/25 */ +@Slf4j(topic = "db") public class Storage { /** @@ -87,6 +90,7 @@ public class Storage { * Default values of directory */ private static final String DEFAULT_DB_ENGINE = "LEVELDB"; + private static final String ROCKS_DB_ENGINE = "ROCKSDB"; private static final boolean DEFAULT_DB_SYNC = false; private static final boolean DEFAULT_EVENT_SUBSCRIBE_CONTRACT_PARSE = true; private static final String DEFAULT_DB_DIRECTORY = "database"; @@ -171,6 +175,11 @@ public class Storage { private final Map dbRoots = Maps.newConcurrentMap(); public static String getDbEngineFromConfig(final Config config) { + if (Arch.isArm64()) { + // if is arm64 but config is leveldb, should throw exception? + logger.warn("Arm64 architecture detected, using RocksDB as db engine, ignore config."); + return ROCKS_DB_ENGINE; + } return config.hasPath(DB_ENGINE_CONFIG_KEY) ? config.getString(DB_ENGINE_CONFIG_KEY) : DEFAULT_DB_ENGINE; } diff --git a/crypto/src/main/java/org/tron/common/crypto/zksnark/Fp12.java b/crypto/src/main/java/org/tron/common/crypto/zksnark/Fp12.java index 26ea708fbe4..f144656b0ba 100644 --- a/crypto/src/main/java/org/tron/common/crypto/zksnark/Fp12.java +++ b/crypto/src/main/java/org/tron/common/crypto/zksnark/Fp12.java @@ -356,7 +356,7 @@ public boolean equals(Object o) { @Override public int hashCode() { - return new Integer(a.hashCode() + b.hashCode()).hashCode(); + return a.hashCode() + b.hashCode(); } @Override diff --git a/crypto/src/main/java/org/tron/common/crypto/zksnark/Fp2.java b/crypto/src/main/java/org/tron/common/crypto/zksnark/Fp2.java index ba2a1ceb477..162a5b13b30 100644 --- a/crypto/src/main/java/org/tron/common/crypto/zksnark/Fp2.java +++ b/crypto/src/main/java/org/tron/common/crypto/zksnark/Fp2.java @@ -164,7 +164,7 @@ public boolean equals(Object o) { @Override public int hashCode() { - return new Integer(a.hashCode() + b.hashCode()).hashCode(); + return a.hashCode() + b.hashCode(); } Fp2 frobeniusMap(int power) { diff --git a/crypto/src/main/java/org/tron/common/crypto/zksnark/Fp6.java b/crypto/src/main/java/org/tron/common/crypto/zksnark/Fp6.java index 0636cc334f1..fb863bb6b34 100644 --- a/crypto/src/main/java/org/tron/common/crypto/zksnark/Fp6.java +++ b/crypto/src/main/java/org/tron/common/crypto/zksnark/Fp6.java @@ -246,6 +246,6 @@ public boolean equals(Object o) { @Override public int hashCode() { - return new Integer(a.hashCode() + b.hashCode() + c.hashCode()).hashCode(); + return a.hashCode() + b.hashCode() + c.hashCode(); } } diff --git a/docker/arm64/Dockerfile b/docker/arm64/Dockerfile new file mode 100644 index 00000000000..6435faf7ead --- /dev/null +++ b/docker/arm64/Dockerfile @@ -0,0 +1,33 @@ +FROM arm64v8/eclipse-temurin:17 + +ENV TMP_DIR="/tron-build" +ENV BASE_DIR="/java-tron" + +RUN set -o errexit -o nounset \ + && apt-get update \ + && apt-get -y install git p7zip-full wget libtcmalloc-minimal4 \ + && echo "git clone" \ + && mkdir -p $TMP_DIR \ + && cd $TMP_DIR \ + && git clone https://github.com/tronprotocol/java-tron.git \ + && cd java-tron \ + && git checkout master \ + && ./gradlew clean build -x test -x check --no-daemon \ + && cd build/distributions \ + && 7za x -y java-tron-1.0.0.zip \ + && mv java-tron-1.0.0 $BASE_DIR \ + && rm -rf $TMP_DIR \ + && rm -rf ~/.gradle \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + +ENV LD_PRELOAD="/usr/lib/aarch64-linux-gnu/libtcmalloc_minimal.so.4" +ENV TCMALLOC_RELEASE_RATE=10 + +RUN wget -P $BASE_DIR/config https://raw.githubusercontent.com/tronprotocol/tron-deployment/master/main_net_config.conf + +COPY docker-entrypoint.sh $BASE_DIR/bin + +WORKDIR $BASE_DIR + +ENTRYPOINT ["./bin/docker-entrypoint.sh"] \ No newline at end of file diff --git a/framework/build.gradle b/framework/build.gradle index 9763f2980e2..7637bfe7899 100644 --- a/framework/build.gradle +++ b/framework/build.gradle @@ -116,6 +116,17 @@ test { destinationFile = file("$buildDir/jacoco/jacocoTest.exec") classDumpDir = file("$buildDir/jacoco/classpathdumps") } + if (rootProject.archInfo.isArm64) { + exclude { element -> + element.file.name.toLowerCase().contains('leveldb') + } + filter { + excludeTestsMatching '*.*leveldb*' + excludeTestsMatching '*.*Leveldb*' + excludeTestsMatching '*.*LevelDB*' + excludeTestsMatching '*.*LevelDb*' + } + } if (isWindows()) { exclude '**/ShieldedTransferActuatorTest.class' exclude '**/BackupDbUtilTest.class' @@ -151,7 +162,7 @@ def binaryRelease(taskName, jarName, mainClass) { // explicit_dependency dependsOn (project(':actuator').jar, project(':consensus').jar, project(':chainbase').jar, - project(':crypto').jar, project(':common').jar, project(':protocol').jar) + project(':crypto').jar, project(':common').jar, project(':protocol').jar, project(':platform').jar) from { configurations.runtimeClasspath.collect { @@ -197,8 +208,7 @@ def createScript(project, mainClass, name) { } } } - -applicationDistribution.from("../gradle/java-tron.vmoptions") { +applicationDistribution.from(rootProject.archInfo.VMOptions) { into "bin" } //distZip { diff --git a/framework/src/main/java/org/tron/core/config/args/Args.java b/framework/src/main/java/org/tron/core/config/args/Args.java index b7c7d95cdae..6f9d7ed4cec 100644 --- a/framework/src/main/java/org/tron/core/config/args/Args.java +++ b/framework/src/main/java/org/tron/core/config/args/Args.java @@ -47,6 +47,7 @@ import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import org.tron.common.arch.Arch; import org.tron.common.args.Account; import org.tron.common.args.GenesisBlock; import org.tron.common.args.Witness; @@ -376,6 +377,7 @@ private static Map getOptionGroup() { * set parameters. */ public static void setParam(final String[] args, final String confFileName) { + Arch.throwUnsupportedJavaException(); JCommander.newBuilder().addObject(PARAMETER).build().parse(args); if (PARAMETER.version) { printVersion(); @@ -1721,11 +1723,13 @@ private static void initRocksDbSettings(Config config) { .getLong(prefix + "targetFileSizeBase") : 64; int targetFileSizeMultiplier = config.hasPath(prefix + "targetFileSizeMultiplier") ? config .getInt(prefix + "targetFileSizeMultiplier") : 1; + int maxOpenFiles = config.hasPath(prefix + "maxOpenFiles") + ? config.getInt(prefix + "maxOpenFiles") : 5000; PARAMETER.rocksDBCustomSettings = RocksDbSettings .initCustomSettings(levelNumber, compactThreads, blocksize, maxBytesForLevelBase, maxBytesForLevelMultiplier, level0FileNumCompactionTrigger, - targetFileSizeBase, targetFileSizeMultiplier); + targetFileSizeBase, targetFileSizeMultiplier, maxOpenFiles); RocksDbSettings.loggingSettings(); } @@ -1762,6 +1766,8 @@ private static void initBackupProperty(Config config) { public static void logConfig() { CommonParameter parameter = CommonParameter.getInstance(); logger.info("\n"); + logger.info("************************ System info ************************"); + logger.info("{}", Arch.withAll()); logger.info("************************ Net config ************************"); logger.info("P2P version: {}", parameter.getNodeP2pVersion()); logger.info("LAN IP: {}", parameter.getNodeLanIp()); diff --git a/framework/src/main/java/org/tron/core/services/http/GetProposalByIdServlet.java b/framework/src/main/java/org/tron/core/services/http/GetProposalByIdServlet.java index a903a5b4920..9d7805d4f98 100644 --- a/framework/src/main/java/org/tron/core/services/http/GetProposalByIdServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/GetProposalByIdServlet.java @@ -26,7 +26,7 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) { try { boolean visible = Util.getVisible(request); String input = request.getParameter("id"); - long id = new Long(input); + long id = Long.parseLong(input); fillResponse(ByteString.copyFrom(ByteArray.fromLong(id)), visible, response); } catch (Exception e) { Util.processError(e, response); diff --git a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java index eb432432a1c..afc401e7e7c 100644 --- a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java +++ b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java @@ -24,7 +24,6 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; -import java.util.regex.Matcher; import java.util.regex.Pattern; import lombok.Getter; import lombok.extern.slf4j.Slf4j; @@ -230,15 +229,11 @@ public static void handleLogsFilter(LogsFilterCapsule logsFilterCapsule) { @Override public String web3ClientVersion() { - Pattern shortVersion = Pattern.compile("(\\d\\.\\d).*"); - Matcher matcher = shortVersion.matcher(System.getProperty("java.version")); - matcher.matches(); - return String.join("/", Arrays.asList( "TRON", "v" + Version.getVersion(), System.getProperty("os.name"), - "Java" + matcher.group(1))); + "Java" + System.getProperty("java.specification.version"))); } @Override diff --git a/framework/src/main/resources/config.conf b/framework/src/main/resources/config.conf index 7603ef6ffbb..55e4b8c55d9 100644 --- a/framework/src/main/resources/config.conf +++ b/framework/src/main/resources/config.conf @@ -5,7 +5,7 @@ net { storage { # Directory for storing persistent data - db.engine = "LEVELDB", + db.engine = "LEVELDB", // deprecated for arm, because arm only support "ROCKSDB". db.sync = false, db.directory = "database", index.directory = "index", @@ -19,7 +19,7 @@ storage { # Otherwise, db configs will remain default and data will be stored in # the path of "output-directory" or which is set by "-d" ("--output-directory"). - # setting can impove leveldb performance .... start + # setting can impove leveldb performance .... start, deprecated for arm # node: if this will increase process fds,you may be check your ulimit if 'too many open files' error occurs # see https://github.com/tronprotocol/tips/blob/master/tip-343.md for detail # if you find block sync has lower performance,you can try this settings @@ -32,21 +32,21 @@ storage { #defaultL = { # maxOpenFiles = 1000 #} - # setting can impove leveldb performance .... end + # setting can impove leveldb performance .... end, deprecated for arm # Attention: name is a required field that must be set !!! properties = [ // { // name = "account", // path = "storage_directory_test", - // createIfMissing = true, + // createIfMissing = true, // deprecated for arm start // paranoidChecks = true, // verifyChecksums = true, // compressionType = 1, // compressed with snappy // blockSize = 4096, // 4 KB = 4 * 1024 B // writeBufferSize = 10485760, // 10 MB = 10 * 1024 * 1024 B // cacheSize = 10485760, // 10 MB = 10 * 1024 * 1024 B - // maxOpenFiles = 100 + // maxOpenFiles = 100 // deprecated for arm end // }, // { // name = "account-index", diff --git a/framework/src/test/java/org/tron/common/storage/leveldb/LevelDbDataSourceImplTest.java b/framework/src/test/java/org/tron/common/storage/leveldb/LevelDbDataSourceImplTest.java index bf18b988f19..c8b29c3020d 100644 --- a/framework/src/test/java/org/tron/common/storage/leveldb/LevelDbDataSourceImplTest.java +++ b/framework/src/test/java/org/tron/common/storage/leveldb/LevelDbDataSourceImplTest.java @@ -28,6 +28,7 @@ import com.google.common.collect.Sets; import java.io.File; import java.io.IOException; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -38,15 +39,24 @@ import java.util.Set; import java.util.stream.Collectors; import lombok.extern.slf4j.Slf4j; +import org.iq80.leveldb.DBException; import org.junit.AfterClass; import org.junit.Assert; import org.junit.Before; import org.junit.ClassRule; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; import org.junit.rules.TemporaryFolder; +import org.rocksdb.RocksDB; +import org.tron.common.parameter.CommonParameter; +import org.tron.common.storage.WriteOptionsWrapper; +import org.tron.common.storage.rocksdb.RocksDbDataSourceImpl; import org.tron.common.utils.ByteArray; import org.tron.common.utils.FileUtil; +import org.tron.common.utils.PropUtil; import org.tron.common.utils.PublicMethod; +import org.tron.common.utils.StorageUtils; import org.tron.core.Constant; import org.tron.core.config.args.Args; import org.tron.core.db2.common.WrappedByteArray; @@ -73,6 +83,14 @@ public class LevelDbDataSourceImplTest { private byte[] key5 = "00000005aa".getBytes(); private byte[] key6 = "00000006aa".getBytes(); + + @Rule + public final ExpectedException exception = ExpectedException.none(); + + static { + RocksDB.loadLibrary(); + } + /** * Release resources. */ @@ -102,8 +120,19 @@ public void testPutGet() { assertNotNull(dataSourceTest.getData(key)); assertEquals(1, dataSourceTest.allKeys().size()); + assertEquals(1, dataSourceTest.getTotal()); + assertEquals(1, dataSourceTest.allValues().size()); assertEquals("50000", ByteArray.toStr(dataSourceTest.getData(key1.getBytes()))); + dataSourceTest.deleteData(key); + assertNull(dataSourceTest.getData(key)); + assertEquals(0, dataSourceTest.getTotal()); + dataSourceTest.iterator().forEachRemaining(entry -> Assert.fail("iterator should be empty")); + dataSourceTest.stream().forEach(entry -> Assert.fail("stream should be empty")); + dataSourceTest.stat(); dataSourceTest.closeDB(); + dataSourceTest.stat(); // stat again + exception.expect(DBException.class); + dataSourceTest.deleteData(key); } @Test @@ -142,6 +171,23 @@ public void testupdateByBatchInner() { assertEquals("50000", ByteArray.toStr(dataSource.getData(key1.getBytes()))); assertEquals("10000", ByteArray.toStr(dataSource.getData(key2.getBytes()))); assertEquals(2, dataSource.allKeys().size()); + + rows.clear(); + rows.put(key1.getBytes(), null); + rows.put(key2.getBytes(), null); + dataSource.updateByBatch(rows, WriteOptionsWrapper.getInstance()); + assertEquals(0, dataSource.allKeys().size()); + + rows.clear(); + rows.put(key1.getBytes(), value1.getBytes()); + rows.put(key2.getBytes(), null); + dataSource.updateByBatch(rows); + assertEquals("50000", ByteArray.toStr(dataSource.getData(key1.getBytes()))); + assertEquals(1, dataSource.allKeys().size()); + rows.clear(); + rows.put(null, null); + exception.expect(RuntimeException.class); + dataSource.updateByBatch(rows); dataSource.closeDB(); } @@ -354,6 +400,118 @@ public void initDbTest() { assertEquals(TronError.ErrCode.LEVELDB_INIT, thrown.getErrCode()); } + @Test + public void testCheckOrInitEngine() { + String dir = + Args.getInstance().getOutputDirectory() + Args.getInstance().getStorage().getDbDirectory(); + String enginePath = dir + File.separator + "test_engine" + File.separator + "engine.properties"; + FileUtil.createDirIfNotExists(dir + File.separator + "test_engine"); + FileUtil.createFileIfNotExists(enginePath); + PropUtil.writeProperty(enginePath, "ENGINE", "LEVELDB"); + Assert.assertEquals("LEVELDB", PropUtil.readProperty(enginePath, "ENGINE")); + + LevelDbDataSourceImpl dataSource; + dataSource = new LevelDbDataSourceImpl(dir, "test_engine"); + dataSource.initDB(); + dataSource.closeDB(); + + System.gc(); + PropUtil.writeProperty(enginePath, "ENGINE", "ROCKSDB"); + Assert.assertEquals("ROCKSDB", PropUtil.readProperty(enginePath, "ENGINE")); + try { + dataSource = new LevelDbDataSourceImpl(dir, "test_engine"); + dataSource.initDB(); + } catch (Exception e) { + Assert.assertEquals(String.format( + "Cannot open RocksDB database '%s' with LevelDB engine." + + " Set db.engine=ROCKSDB or use LevelDB database. ", "test_engine"), + e.getMessage()); + } + } + + @Test + public void testLevelDbOpenRocksDb() { + String name = "test_openRocksDb"; + String output = Paths + .get(StorageUtils.getOutputDirectoryByDbName(name), CommonParameter + .getInstance().getStorage().getDbDirectory()).toString(); + RocksDbDataSourceImpl rocksDb = new RocksDbDataSourceImpl(output, name); + rocksDb.initDB(); + rocksDb.putData(key1, value1); + rocksDb.closeDB(); + LevelDbDataSourceImpl levelDB = + new LevelDbDataSourceImpl(StorageUtils.getOutputDirectoryByDbName(name), name); + exception.expectMessage(String.format( + "Cannot open RocksDB database '%s' with LevelDB engine." + + " Set db.engine=ROCKSDB or use LevelDB database. ", name)); + levelDB.initDB(); + } + + @Test + public void testNewInstance() { + dataSourceTest.closeDB(); + LevelDbDataSourceImpl newInst = dataSourceTest.newInstance(); + newInst.initDB(); + assertFalse(newInst.flush()); + newInst.closeDB(); + LevelDbDataSourceImpl empty = new LevelDbDataSourceImpl(); + empty.setDBName("empty"); + assertEquals("empty", empty.getDBName()); + String name = "newInst2"; + LevelDbDataSourceImpl newInst2 = new LevelDbDataSourceImpl( + StorageUtils.getOutputDirectoryByDbName(name), + name); + newInst2.initDB(); + newInst2.closeDB(); + } + + @Test + public void testGetNext() { + LevelDbDataSourceImpl dataSource = new LevelDbDataSourceImpl( + Args.getInstance().getOutputDirectory(), "test_getNext_key"); + dataSource.initDB(); + dataSource.resetDb(); + putSomeKeyValue(dataSource); + // case: normal + Map seekKvLimitNext = dataSource.getNext("0000000300".getBytes(), 2); + Map hashMap = Maps.newHashMap(); + hashMap.put(ByteArray.toStr(key3), ByteArray.toStr(value3)); + hashMap.put(ByteArray.toStr(key4), ByteArray.toStr(value4)); + seekKvLimitNext.forEach((key, value) -> { + String keyStr = ByteArray.toStr(key); + Assert.assertTrue("getNext", hashMap.containsKey(keyStr)); + Assert.assertEquals(ByteArray.toStr(value), hashMap.get(keyStr)); + }); + // case: targetKey greater than all existed keys + seekKvLimitNext = dataSource.getNext("0000000700".getBytes(), 2); + Assert.assertEquals(0, seekKvLimitNext.size()); + // case: limit<=0 + seekKvLimitNext = dataSource.getNext("0000000300".getBytes(), 0); + Assert.assertEquals(0, seekKvLimitNext.size()); + dataSource.resetDb(); + dataSource.closeDB(); + } + + @Test + public void testGetlatestValues() { + LevelDbDataSourceImpl dataSource = new LevelDbDataSourceImpl( + Args.getInstance().getOutputDirectory(), "test_getlatestValues_key"); + dataSource.initDB(); + dataSource.resetDb(); + putSomeKeyValue(dataSource); + // case: normal + Set seekKeyLimitNext = dataSource.getlatestValues(2); + Set hashSet = Sets.newHashSet(ByteArray.toStr(value5), ByteArray.toStr(value6)); + seekKeyLimitNext.forEach(value -> { + Assert.assertTrue(hashSet.contains(ByteArray.toStr(value))); + }); + // case: limit<=0 + seekKeyLimitNext = dataSource.getlatestValues(0); + assertEquals(0, seekKeyLimitNext.size()); + dataSource.resetDb(); + dataSource.closeDB(); + } + private void makeExceptionDb(String dbName) { LevelDbDataSourceImpl dataSource = new LevelDbDataSourceImpl( Args.getInstance().getOutputDirectory(), "test_initDb"); diff --git a/framework/src/test/java/org/tron/common/storage/leveldb/RocksDbDataSourceImplTest.java b/framework/src/test/java/org/tron/common/storage/leveldb/RocksDbDataSourceImplTest.java index c6fce30e3af..8f42c44e3b9 100644 --- a/framework/src/test/java/org/tron/common/storage/leveldb/RocksDbDataSourceImplTest.java +++ b/framework/src/test/java/org/tron/common/storage/leveldb/RocksDbDataSourceImplTest.java @@ -10,6 +10,8 @@ import com.google.common.collect.Sets; import java.io.File; import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -24,13 +26,20 @@ import org.junit.Assert; import org.junit.BeforeClass; import org.junit.ClassRule; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; import org.junit.rules.TemporaryFolder; +import org.rocksdb.RocksDBException; +import org.tron.common.error.TronDBException; +import org.tron.common.parameter.CommonParameter; +import org.tron.common.storage.WriteOptionsWrapper; import org.tron.common.storage.rocksdb.RocksDbDataSourceImpl; import org.tron.common.utils.ByteArray; import org.tron.common.utils.FileUtil; import org.tron.common.utils.PropUtil; import org.tron.common.utils.PublicMethod; +import org.tron.common.utils.StorageUtils; import org.tron.core.config.args.Args; import org.tron.core.db2.common.WrappedByteArray; import org.tron.core.exception.TronError; @@ -55,6 +64,9 @@ public class RocksDbDataSourceImplTest { private byte[] key5 = "00000005aa".getBytes(); private byte[] key6 = "00000006aa".getBytes(); + @Rule + public final ExpectedException expectedException = ExpectedException.none(); + /** * Release resources. */ @@ -84,8 +96,18 @@ public void testPutGet() { assertNotNull(dataSourceTest.getData(key)); assertEquals(1, dataSourceTest.allKeys().size()); + assertEquals(1, dataSourceTest.getTotal()); + assertEquals(1, dataSourceTest.allValues().size()); assertEquals("50000", ByteArray.toStr(dataSourceTest.getData(key1.getBytes()))); + dataSourceTest.deleteData(key); + assertNull(dataSourceTest.getData(key)); + assertEquals(0, dataSourceTest.getTotal()); + dataSourceTest.iterator().forEachRemaining(entry -> Assert.fail("iterator should be empty")); + dataSourceTest.stat(); dataSourceTest.closeDB(); + dataSourceTest.stat(); // stat again + expectedException.expect(TronDBException.class); + dataSourceTest.deleteData(key); } @Test @@ -124,6 +146,23 @@ public void testupdateByBatchInner() { assertEquals("50000", ByteArray.toStr(dataSource.getData(key1.getBytes()))); assertEquals("10000", ByteArray.toStr(dataSource.getData(key2.getBytes()))); assertEquals(2, dataSource.allKeys().size()); + + rows.clear(); + rows.put(key1.getBytes(), null); + rows.put(key2.getBytes(), null); + dataSource.updateByBatch(rows, WriteOptionsWrapper.getInstance()); + assertEquals(0, dataSource.allKeys().size()); + + rows.clear(); + rows.put(key1.getBytes(), value1.getBytes()); + rows.put(key2.getBytes(), null); + dataSource.updateByBatch(rows); + assertEquals("50000", ByteArray.toStr(dataSource.getData(key1.getBytes()))); + assertEquals(1, dataSource.allKeys().size()); + rows.clear(); + rows.put(null, null); + expectedException.expect(RuntimeException.class); + dataSource.updateByBatch(rows); dataSource.closeDB(); } @@ -276,8 +315,9 @@ public void testCheckOrInitEngine() { try { dataSource.initDB(); } catch (Exception e) { - Assert.assertEquals(String.format("failed to check database: %s, engine do not match", - "test_engine"), + Assert.assertEquals(String.format( + "Cannot open LevelDB database '%s' with RocksDB engine." + + " Set db.engine=LEVELDB or use RocksDB database. ", "test_engine"), e.getMessage()); } Assert.assertNull(dataSource.getDatabase()); @@ -396,6 +436,103 @@ public void initDbTest() { assertEquals(TronError.ErrCode.ROCKSDB_INIT, thrown.getErrCode()); } + @Test + public void testRocksDbOpenLevelDb() { + String name = "test_openLevelDb"; + String output = Paths + .get(StorageUtils.getOutputDirectoryByDbName(name), CommonParameter + .getInstance().getStorage().getDbDirectory()).toString(); + LevelDbDataSourceImpl levelDb = new LevelDbDataSourceImpl( + StorageUtils.getOutputDirectoryByDbName(name), name); + levelDb.initDB(); + levelDb.putData(key1, value1); + levelDb.closeDB(); + RocksDbDataSourceImpl rocksDb = new RocksDbDataSourceImpl(output, name); + expectedException.expectMessage( + String.format( + "Cannot open LevelDB database '%s' with RocksDB engine." + + " Set db.engine=LEVELDB or use RocksDB database. ", name)); + rocksDb.initDB(); + } + + @Test + public void testRocksDbOpenLevelDb2() { + String name = "test_openLevelDb2"; + String output = Paths + .get(StorageUtils.getOutputDirectoryByDbName(name), CommonParameter + .getInstance().getStorage().getDbDirectory()).toString(); + LevelDbDataSourceImpl levelDb = new LevelDbDataSourceImpl( + StorageUtils.getOutputDirectoryByDbName(name), name); + levelDb.initDB(); + levelDb.putData(key1, value1); + levelDb.closeDB(); + // delete engine.properties file to simulate the case that db.engine is not set. + File engineFile = Paths + .get(output, name, "engine.properties").toFile(); + if (engineFile.exists()) { + engineFile.delete(); + } + Assert.assertFalse(engineFile.exists()); + RocksDbDataSourceImpl rocksDb = new RocksDbDataSourceImpl(output, name); + expectedException.expectMessage( + String.format( + "Cannot open LevelDB database '%s' with RocksDB engine." + + " Set db.engine=LEVELDB or use RocksDB database. ", name)); + rocksDb.initDB(); + } + + @Test + public void testNewInstance() { + dataSourceTest.closeDB(); + RocksDbDataSourceImpl newInst = dataSourceTest.newInstance(); + newInst.initDB(); + assertFalse(newInst.flush()); + newInst.closeDB(); + RocksDbDataSourceImpl empty = new RocksDbDataSourceImpl(); + empty.setDBName("empty"); + assertEquals("empty", empty.getDBName()); + String output = Paths + .get(StorageUtils.getOutputDirectoryByDbName("newInst2"), CommonParameter + .getInstance().getStorage().getDbDirectory()).toString(); + RocksDbDataSourceImpl newInst2 = new RocksDbDataSourceImpl(output, "newInst2"); + newInst2.initDB(); + newInst2.closeDB(); + } + + @Test + public void backupAndDelete() throws RocksDBException { + RocksDbDataSourceImpl dataSource = new RocksDbDataSourceImpl( + Args.getInstance().getOutputDirectory(), "backupAndDelete"); + dataSource.initDB(); + putSomeKeyValue(dataSource); + Path dir = Paths.get(Args.getInstance().getOutputDirectory(), "backup"); + String path = dir + File.separator; + FileUtil.createDirIfNotExists(path); + dataSource.backup(path); + File backDB = Paths.get(dir.toString(),dataSource.getDBName()).toFile(); + Assert.assertTrue(backDB.exists()); + dataSource.deleteDbBakPath(path); + Assert.assertFalse(backDB.exists()); + dataSource.closeDB(); + } + + @Test + public void testGetTotal() { + RocksDbDataSourceImpl dataSource = new RocksDbDataSourceImpl( + Args.getInstance().getOutputDirectory(), "test_getTotal_key"); + dataSource.initDB(); + dataSource.resetDb(); + + Map dataMapset = Maps.newHashMap(); + dataMapset.put(key1, value1); + dataMapset.put(key2, value2); + dataMapset.put(key3, value3); + dataMapset.forEach(dataSource::putData); + Assert.assertEquals(dataMapset.size(), dataSource.getTotal()); + dataSource.resetDb(); + dataSource.closeDB(); + } + private void makeExceptionDb(String dbName) { RocksDbDataSourceImpl dataSource = new RocksDbDataSourceImpl( Args.getInstance().getOutputDirectory(), "test_initDb"); diff --git a/framework/src/test/java/org/tron/common/utils/HashCodeTest.java b/framework/src/test/java/org/tron/common/utils/HashCodeTest.java new file mode 100644 index 00000000000..36f9435c1aa --- /dev/null +++ b/framework/src/test/java/org/tron/common/utils/HashCodeTest.java @@ -0,0 +1,23 @@ +package org.tron.common.utils; + +import java.util.Objects; +import org.junit.Assert; +import org.junit.Test; +import org.tron.core.capsule.AccountCapsule; +import org.tron.core.vm.repository.Type; +import org.tron.core.vm.repository.Value; +import org.tron.protos.Protocol; + +public class HashCodeTest { + + @Test + public void test() { + Type type = new Type(); + type.setType(Type.NORMAL); + Assert.assertEquals(Integer.valueOf(Type.NORMAL).hashCode(), type.hashCode()); + Protocol.Account account = Protocol.Account.newBuilder().setBalance(100).build(); + Value value = Value.create(new AccountCapsule(account.toByteArray())); + Assert.assertEquals(Integer.valueOf( + type.hashCode() + Objects.hashCode(account)).hashCode(), value.hashCode()); + } +} diff --git a/framework/src/test/java/org/tron/core/capsule/utils/ExchangeProcessorTest.java b/framework/src/test/java/org/tron/core/capsule/utils/ExchangeProcessorTest.java index 717c62b01a8..1f0be4b1f7c 100644 --- a/framework/src/test/java/org/tron/core/capsule/utils/ExchangeProcessorTest.java +++ b/framework/src/test/java/org/tron/core/capsule/utils/ExchangeProcessorTest.java @@ -138,12 +138,63 @@ public void testWithdraw() { @Test public void testStrictMath() { long supply = 1_000_000_000_000_000_000L; - ExchangeProcessor processor = new ExchangeProcessor(supply, false); - long anotherTokenQuant = processor.exchange(4732214, 2202692725330L, 29218); - processor = new ExchangeProcessor(supply, true); - long result = processor.exchange(4732214, 2202692725330L, 29218); - Assert.assertNotEquals(anotherTokenQuant, result); + long[][] testData = { + {4732214L, 2202692725330L, 29218L}, + {5618633L, 556559904655L, 1L}, + {9299554L, 1120271441185L, 7000L}, + {62433133L, 12013267997895L, 100000L}, + {64212664L, 725836766395L, 50000L}, + {64126212L, 2895100109660L, 5000L}, + {56459055L, 3288380567368L, 165000L}, + {21084707L, 1589204008960L, 50000L}, + {24120521L, 1243764649177L, 20000L}, + {836877L, 212532333234L, 5293L}, + {55879741L, 13424854054078L, 250000L}, + {66388882L, 11300012790454L, 300000L}, + {94470955L, 7941038150919L, 2000L}, + {13613746L, 5012660712983L, 122L}, + {71852829L, 5262251868618L, 396L}, + {3857658L, 446109245044L, 20637L}, + {35491863L, 3887393269796L, 100L}, + {295632118L, 1265298439004L, 500000L}, + {49320113L, 1692106302503L, 123267L}, + {10966984L, 6222910652894L, 2018L}, + {41634280L, 2004508994767L, 865L}, + {10087714L, 6765558834714L, 1009L}, + {42270078L, 210360843525L, 200000L}, + {571091915L, 655011397250L, 2032520L}, + {51026781L, 1635726339365L, 37L}, + {61594L, 312318864132L, 500L}, + {11616684L, 5875978057357L, 20L}, + {60584529L, 1377717821301L, 78132L}, + {29818073L, 3033545989651L, 182L}, + {3855280L, 834647482043L, 16L}, + {58310711L, 1431562205655L, 200000L}, + {60226263L, 1386036785882L, 178226L}, + {3537634L, 965771433992L, 225L}, + {3760534L, 908700758784L, 328L}, + {80913L, 301864126445L, 4L}, + {3789271L, 901842209723L, 1L}, + {4051904L, 843419481286L, 1005L}, + {89141L, 282107742510L, 100L}, + {90170L, 282854635378L, 26L}, + {4229852L, 787503315944L, 137L}, + {4259884L, 781975090197L, 295L}, + {3627657L, 918682223700L, 34L}, + {813519L, 457546358759L, 173L}, + {89626L, 327856173057L, 27L}, + {97368L, 306386489550L, 50L}, + {93712L, 305866015731L, 4L}, + {3281260L, 723656594544L, 40L}, + {3442652L, 689908773685L, 18L}, + }; + + for (long[] data : testData) { + ExchangeProcessor processor = new ExchangeProcessor(supply, false); + long anotherTokenQuant = processor.exchange(data[0], data[1], data[2]); + processor = new ExchangeProcessor(supply, true); + long result = processor.exchange(data[0], data[1], data[2]); + Assert.assertNotEquals(anotherTokenQuant, result); + } } - - } diff --git a/framework/src/test/java/org/tron/core/db2/CheckpointV2Test.java b/framework/src/test/java/org/tron/core/db2/CheckpointV2Test.java index dff2d376fd5..fb7f1987e9f 100644 --- a/framework/src/test/java/org/tron/core/db2/CheckpointV2Test.java +++ b/framework/src/test/java/org/tron/core/db2/CheckpointV2Test.java @@ -4,7 +4,7 @@ import com.google.common.primitives.Bytes; import com.google.common.primitives.Longs; import com.google.protobuf.ByteString; -import java.io.File; +import java.io.IOException; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -13,11 +13,12 @@ import org.junit.After; import org.junit.Assert; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.TemporaryFolder; import org.tron.common.application.Application; import org.tron.common.application.ApplicationFactory; import org.tron.common.application.TronApplicationContext; -import org.tron.common.utils.FileUtil; import org.tron.common.utils.Sha256Hash; import org.tron.core.Constant; import org.tron.core.capsule.BlockCapsule; @@ -34,10 +35,12 @@ public class CheckpointV2Test { private TronApplicationContext context; private Application appT; private TestRevokingTronStore tronDatabase; + @Rule + public final TemporaryFolder temporaryFolder = new TemporaryFolder(); @Before - public void init() { - Args.setParam(new String[]{"-d", "output_SnapshotManager_test"}, + public void init() throws IOException { + Args.setParam(new String[]{"-d", temporaryFolder.newFolder().toString()}, Constant.TEST_CONF); Args.getInstance().getStorage().setCheckpointVersion(2); Args.getInstance().getStorage().setCheckpointSync(true); @@ -54,9 +57,6 @@ public void removeDb() { Args.clearParam(); context.destroy(); tronDatabase.close(); - FileUtil.deleteDir(new File("output_SnapshotManager_test")); - revokingDatabase.getCheckTmpStore().close(); - tronDatabase.close(); } @Test diff --git a/framework/src/test/java/org/tron/core/db2/RevokingDbWithCacheNewValueTest.java b/framework/src/test/java/org/tron/core/db2/RevokingDbWithCacheNewValueTest.java index 2290df86978..f371f2348a7 100644 --- a/framework/src/test/java/org/tron/core/db2/RevokingDbWithCacheNewValueTest.java +++ b/framework/src/test/java/org/tron/core/db2/RevokingDbWithCacheNewValueTest.java @@ -1,22 +1,22 @@ package org.tron.core.db2; -import java.io.File; +import java.io.IOException; import java.util.Arrays; import java.util.List; import java.util.Set; import java.util.stream.Collectors; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.ArrayUtils; -import org.apache.commons.lang3.RandomStringUtils; import org.junit.After; import org.junit.Assert; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.TemporaryFolder; import org.tron.common.application.Application; import org.tron.common.application.ApplicationFactory; import org.tron.common.application.TronApplicationContext; import org.tron.common.utils.ByteArray; -import org.tron.common.utils.FileUtil; import org.tron.common.utils.SessionOptional; import org.tron.core.Constant; import org.tron.core.capsule.utils.MarketUtils; @@ -34,12 +34,14 @@ public class RevokingDbWithCacheNewValueTest { private TronApplicationContext context; private Application appT; private TestRevokingTronStore tronDatabase; + @Rule + public final TemporaryFolder temporaryFolder = new TemporaryFolder(); private String databasePath = ""; @Before - public void init() { - databasePath = "output_revokingStore_test_" + RandomStringUtils.randomAlphanumeric(10); + public void init() throws IOException { + databasePath = temporaryFolder.newFolder().toString(); Args.setParam(new String[]{"-d", databasePath}, Constant.TEST_CONF); context = new TronApplicationContext(DefaultConfig.class); @@ -51,7 +53,6 @@ public void removeDb() { Args.clearParam(); context.destroy(); tronDatabase.close(); - FileUtil.deleteDir(new File(databasePath)); } @Test diff --git a/framework/src/test/java/org/tron/core/db2/SnapshotImplTest.java b/framework/src/test/java/org/tron/core/db2/SnapshotImplTest.java index aab6f656b1f..649056aa151 100644 --- a/framework/src/test/java/org/tron/core/db2/SnapshotImplTest.java +++ b/framework/src/test/java/org/tron/core/db2/SnapshotImplTest.java @@ -3,16 +3,16 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; -import java.io.File; +import java.io.IOException; import java.lang.reflect.Constructor; import org.junit.After; -import org.junit.Assert; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.TemporaryFolder; import org.tron.common.application.Application; import org.tron.common.application.ApplicationFactory; import org.tron.common.application.TronApplicationContext; -import org.tron.common.utils.FileUtil; import org.tron.core.Constant; import org.tron.core.config.DefaultConfig; import org.tron.core.config.args.Args; @@ -26,10 +26,12 @@ public class SnapshotImplTest { private TronApplicationContext context; private Application appT; private SnapshotManager revokingDatabase; + @Rule + public final TemporaryFolder temporaryFolder = new TemporaryFolder(); @Before - public void init() { - Args.setParam(new String[]{"-d", "output_revokingStore_test"}, Constant.TEST_CONF); + public void init() throws IOException { + Args.setParam(new String[]{"-d", temporaryFolder.newFolder().toString()}, Constant.TEST_CONF); context = new TronApplicationContext(DefaultConfig.class); appT = ApplicationFactory.create(context); @@ -44,10 +46,7 @@ public void init() { public void removeDb() { Args.clearParam(); context.destroy(); - FileUtil.deleteDir(new File("output_revokingStore_test")); - tronDatabase.close(); - revokingDatabase.shutdown(); } /** diff --git a/framework/src/test/java/org/tron/core/db2/SnapshotManagerTest.java b/framework/src/test/java/org/tron/core/db2/SnapshotManagerTest.java index 134dc99e51c..d6fd319e6a0 100644 --- a/framework/src/test/java/org/tron/core/db2/SnapshotManagerTest.java +++ b/framework/src/test/java/org/tron/core/db2/SnapshotManagerTest.java @@ -6,7 +6,7 @@ import com.google.common.collect.Maps; import com.google.common.primitives.Longs; import com.google.protobuf.ByteString; -import java.io.File; +import java.io.IOException; import java.util.Arrays; import java.util.List; import java.util.Map; @@ -15,11 +15,12 @@ import org.junit.After; import org.junit.Assert; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.TemporaryFolder; import org.tron.common.application.Application; import org.tron.common.application.ApplicationFactory; import org.tron.common.application.TronApplicationContext; -import org.tron.common.utils.FileUtil; import org.tron.common.utils.Sha256Hash; import org.tron.core.Constant; import org.tron.core.capsule.BlockCapsule; @@ -40,11 +41,13 @@ public class SnapshotManagerTest { private TronApplicationContext context; private Application appT; private TestRevokingTronStore tronDatabase; + @Rule + public final TemporaryFolder temporaryFolder = new TemporaryFolder(); @Before - public void init() { - Args.setParam(new String[]{"-d", "output_SnapshotManager_test"}, + public void init() throws IOException { + Args.setParam(new String[]{"-d", temporaryFolder.newFolder().toString()}, Constant.TEST_CONF); context = new TronApplicationContext(DefaultConfig.class); appT = ApplicationFactory.create(context); @@ -59,9 +62,6 @@ public void removeDb() { Args.clearParam(); context.destroy(); tronDatabase.close(); - FileUtil.deleteDir(new File("output_SnapshotManager_test")); - revokingDatabase.getCheckTmpStore().close(); - tronDatabase.close(); } @Test diff --git a/framework/src/test/java/org/tron/core/db2/SnapshotRootTest.java b/framework/src/test/java/org/tron/core/db2/SnapshotRootTest.java index 70b4d9eff30..635cc018cc2 100644 --- a/framework/src/test/java/org/tron/core/db2/SnapshotRootTest.java +++ b/framework/src/test/java/org/tron/core/db2/SnapshotRootTest.java @@ -1,10 +1,13 @@ package org.tron.core.db2; import com.google.common.collect.Sets; -import java.io.File; +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; import lombok.AllArgsConstructor; @@ -13,7 +16,9 @@ import org.junit.After; import org.junit.Assert; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.TemporaryFolder; import org.springframework.util.CollectionUtils; import org.tron.common.application.Application; import org.tron.common.application.ApplicationFactory; @@ -45,11 +50,13 @@ public class SnapshotRootTest { "exchange","market_order","account-trace","contract-state","trans")); private Set allDBNames; private Set allRevokingDBNames; + @Rule + public final TemporaryFolder temporaryFolder = new TemporaryFolder(); @Before - public void init() { - Args.setParam(new String[]{"-d", "output_revokingStore_test"}, Constant.TEST_CONF); + public void init() throws IOException { + Args.setParam(new String[]{"-d", temporaryFolder.newFolder().toString()}, Constant.TEST_CONF); context = new TronApplicationContext(DefaultConfig.class); appT = ApplicationFactory.create(context); } @@ -58,7 +65,6 @@ public void init() { public void removeDb() { Args.clearParam(); context.destroy(); - FileUtil.deleteDir(new File("output_revokingStore_test")); } @Test @@ -133,7 +139,9 @@ public void testSecondCacheCheck() throws ItemNotFoundException { revokingDatabase = context.getBean(SnapshotManager.class); allRevokingDBNames = parseRevokingDBNames(context); - allDBNames = Arrays.stream(new File("output_revokingStore_test/database").list()) + Path path = Paths.get(Args.getInstance().getOutputDirectory(), + Args.getInstance().getStorage().getDbDirectory()); + allDBNames = Arrays.stream(Objects.requireNonNull(path.toFile().list())) .collect(Collectors.toSet()); if (CollectionUtils.isEmpty(allDBNames)) { throw new ItemNotFoundException("No DBs found"); @@ -152,10 +160,13 @@ public void testSecondCacheCheckAddDb() revokingDatabase = context.getBean(SnapshotManager.class); allRevokingDBNames = parseRevokingDBNames(context); allRevokingDBNames.add("secondCheckTestDB"); - FileUtil.createDirIfNotExists("output_revokingStore_test/database/secondCheckTestDB"); - allDBNames = Arrays.stream(new File("output_revokingStore_test/database").list()) + Path path = Paths.get(Args.getInstance().getOutputDirectory(), + Args.getInstance().getStorage().getDbDirectory()); + Path secondCheckTestDB = Paths.get(path.toString(), "secondCheckTestDB"); + FileUtil.createDirIfNotExists(secondCheckTestDB.toString()); + allDBNames = Arrays.stream(Objects.requireNonNull(path.toFile().list())) .collect(Collectors.toSet()); - FileUtil.deleteDir(new File("output_revokingStore_test/database/secondCheckTestDB")); + FileUtil.deleteDir(secondCheckTestDB.toFile()); if (CollectionUtils.isEmpty(allDBNames)) { throw new ItemNotFoundException("No DBs found"); } diff --git a/framework/src/test/java/org/tron/core/jsonrpc/ApiUtilTest.java b/framework/src/test/java/org/tron/core/jsonrpc/ApiUtilTest.java index 570e7ed3498..2c9afbac99b 100644 --- a/framework/src/test/java/org/tron/core/jsonrpc/ApiUtilTest.java +++ b/framework/src/test/java/org/tron/core/jsonrpc/ApiUtilTest.java @@ -4,10 +4,13 @@ import static org.tron.keystore.Wallet.generateRandomBytes; import com.google.protobuf.ByteString; +import org.junit.AfterClass; import org.junit.Assert; +import org.junit.BeforeClass; import org.junit.Test; import org.tron.common.utils.ByteArray; import org.tron.core.capsule.BlockCapsule; +import org.tron.core.config.args.Args; import org.tron.core.services.jsonrpc.JsonRpcApiUtil; import org.tron.protos.Protocol.Block; import org.tron.protos.Protocol.BlockHeader; @@ -16,6 +19,17 @@ public class ApiUtilTest { + + @BeforeClass + public static void init() { + Args.setParam(new String[]{}, "config-localtest.conf"); + } + + @AfterClass + public static void clear() { + Args.clearParam(); + } + @Test public void testGetBlockID() { byte[] mockedHash = generateRandomBytes(128); diff --git a/framework/src/test/java/org/tron/core/jsonrpc/JsonrpcServiceTest.java b/framework/src/test/java/org/tron/core/jsonrpc/JsonrpcServiceTest.java index 0f2214c5c9c..df0e13d6b6c 100644 --- a/framework/src/test/java/org/tron/core/jsonrpc/JsonrpcServiceTest.java +++ b/framework/src/test/java/org/tron/core/jsonrpc/JsonrpcServiceTest.java @@ -434,7 +434,8 @@ public void testGetByJsonBlockId() { getByJsonBlockId("0xxabc", wallet); Assert.fail("Expected to be thrown"); } catch (Exception e) { - Assert.assertEquals("For input string: \"xabc\"", e.getMessage()); + // https://bugs.openjdk.org/browse/JDK-8176425, from JDK 12, the exception message is changed + Assert.assertTrue(e.getMessage().startsWith("For input string: \"xabc\"")); } } @@ -968,4 +969,15 @@ public void testNewFilterFinalizedBlock() { Assert.assertEquals("invalid block range params", e.getMessage()); } } + + @Test + public void testWeb3ClientVersion() { + try { + String[] versions = tronJsonRpc.web3ClientVersion().split("/"); + String javaVersion = versions[versions.length - 1]; + Assert.assertTrue("Java1.8".equals(javaVersion) || "Java17".equals(javaVersion)); + } catch (Exception e) { + Assert.fail(); + } + } } diff --git a/framework/src/test/java/org/tron/core/net/BaseNet.java b/framework/src/test/java/org/tron/core/net/BaseNet.java index 65771bae952..fc04e210acd 100644 --- a/framework/src/test/java/org/tron/core/net/BaseNet.java +++ b/framework/src/test/java/org/tron/core/net/BaseNet.java @@ -16,6 +16,7 @@ import java.io.File; import java.io.IOException; import java.util.Collection; +import java.util.Objects; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; @@ -123,10 +124,12 @@ public static void init() throws Exception { @AfterClass public static void destroy() { - Collection peerConnections = ReflectUtils - .invokeMethod(tronNetDelegate, "getActivePeer"); - for (PeerConnection peer : peerConnections) { - peer.getChannel().close(); + if (Objects.nonNull(tronNetDelegate)) { + Collection peerConnections = ReflectUtils + .invokeMethod(tronNetDelegate, "getActivePeer"); + for (PeerConnection peer : peerConnections) { + peer.getChannel().close(); + } } Args.clearParam(); context.destroy(); diff --git a/framework/src/test/java/org/tron/core/services/RpcApiServicesTest.java b/framework/src/test/java/org/tron/core/services/RpcApiServicesTest.java index 3ae090d3caf..3af0b8fb9b2 100644 --- a/framework/src/test/java/org/tron/core/services/RpcApiServicesTest.java +++ b/framework/src/test/java/org/tron/core/services/RpcApiServicesTest.java @@ -60,8 +60,6 @@ import org.tron.core.config.DefaultConfig; import org.tron.core.config.args.Args; import org.tron.core.db.Manager; -import org.tron.core.services.interfaceOnPBFT.RpcApiServiceOnPBFT; -import org.tron.core.services.interfaceOnSolidity.RpcApiServiceOnSolidity; import org.tron.protos.Protocol; import org.tron.protos.Protocol.Account; import org.tron.protos.Protocol.Block; @@ -144,11 +142,11 @@ public static void init() throws IOException { getInstance().setMetricsPrometheusPort(PublicMethod.chooseRandomPort()); getInstance().setMetricsPrometheusEnable(true); getInstance().setP2pDisable(true); - String fullNode = String.format("%s:%d", getInstance().getNodeLanIp(), + String fullNode = String.format("%s:%d", Constant.LOCAL_HOST, getInstance().getRpcPort()); - String solidityNode = String.format("%s:%d", getInstance().getNodeLanIp(), + String solidityNode = String.format("%s:%d", Constant.LOCAL_HOST, getInstance().getRpcOnSolidityPort()); - String pBFTNode = String.format("%s:%d", getInstance().getNodeLanIp(), + String pBFTNode = String.format("%s:%d", Constant.LOCAL_HOST, getInstance().getRpcOnPBFTPort()); ManagedChannel channelFull = ManagedChannelBuilder.forTarget(fullNode) diff --git a/framework/src/test/java/org/tron/core/services/filter/LiteFnQueryGrpcInterceptorTest.java b/framework/src/test/java/org/tron/core/services/filter/LiteFnQueryGrpcInterceptorTest.java index 84869ea0750..d98e2c9267e 100644 --- a/framework/src/test/java/org/tron/core/services/filter/LiteFnQueryGrpcInterceptorTest.java +++ b/framework/src/test/java/org/tron/core/services/filter/LiteFnQueryGrpcInterceptorTest.java @@ -25,9 +25,6 @@ import org.tron.core.Constant; import org.tron.core.config.DefaultConfig; import org.tron.core.config.args.Args; -import org.tron.core.services.RpcApiService; -import org.tron.core.services.interfaceOnPBFT.RpcApiServiceOnPBFT; -import org.tron.core.services.interfaceOnSolidity.RpcApiServiceOnSolidity; @Slf4j public class LiteFnQueryGrpcInterceptorTest { @@ -62,11 +59,11 @@ public static void init() throws IOException { Args.getInstance().setRpcPBFTEnable(true); Args.getInstance().setRpcOnPBFTPort(PublicMethod.chooseRandomPort()); Args.getInstance().setP2pDisable(true); - String fullnode = String.format("%s:%d", Args.getInstance().getNodeLanIp(), + String fullnode = String.format("%s:%d", Constant.LOCAL_HOST, Args.getInstance().getRpcPort()); - String solidityNode = String.format("%s:%d", Args.getInstance().getNodeLanIp(), + String solidityNode = String.format("%s:%d", Constant.LOCAL_HOST, Args.getInstance().getRpcOnSolidityPort()); - String pBFTNode = String.format("%s:%d", Args.getInstance().getNodeLanIp(), + String pBFTNode = String.format("%s:%d", Constant.LOCAL_HOST, Args.getInstance().getRpcOnPBFTPort()); channelFull = ManagedChannelBuilder.forTarget(fullnode) .usePlaintext() diff --git a/framework/src/test/java/org/tron/core/services/filter/RpcApiAccessInterceptorTest.java b/framework/src/test/java/org/tron/core/services/filter/RpcApiAccessInterceptorTest.java index 900ca304e7d..ce7efabef0c 100644 --- a/framework/src/test/java/org/tron/core/services/filter/RpcApiAccessInterceptorTest.java +++ b/framework/src/test/java/org/tron/core/services/filter/RpcApiAccessInterceptorTest.java @@ -36,8 +36,6 @@ import org.tron.core.config.DefaultConfig; import org.tron.core.config.args.Args; import org.tron.core.services.RpcApiService; -import org.tron.core.services.interfaceOnPBFT.RpcApiServiceOnPBFT; -import org.tron.core.services.interfaceOnSolidity.RpcApiServiceOnSolidity; import org.tron.protos.Protocol.Transaction; @Slf4j @@ -63,11 +61,11 @@ public static void init() throws IOException { Args.getInstance().setRpcPBFTEnable(true); Args.getInstance().setRpcOnPBFTPort(PublicMethod.chooseRandomPort()); Args.getInstance().setP2pDisable(true); - String fullNode = String.format("%s:%d", Args.getInstance().getNodeLanIp(), + String fullNode = String.format("%s:%d", Constant.LOCAL_HOST, Args.getInstance().getRpcPort()); - String solidityNode = String.format("%s:%d", Args.getInstance().getNodeLanIp(), + String solidityNode = String.format("%s:%d", Constant.LOCAL_HOST, Args.getInstance().getRpcOnSolidityPort()); - String pBFTNode = String.format("%s:%d", Args.getInstance().getNodeLanIp(), + String pBFTNode = String.format("%s:%d", Constant.LOCAL_HOST, Args.getInstance().getRpcOnPBFTPort()); ManagedChannel channelFull = ManagedChannelBuilder.forTarget(fullNode) diff --git a/framework/src/test/java/org/tron/keystroe/CredentialsTest.java b/framework/src/test/java/org/tron/keystroe/CredentialsTest.java index ce992c3443f..2642129e00a 100644 --- a/framework/src/test/java/org/tron/keystroe/CredentialsTest.java +++ b/framework/src/test/java/org/tron/keystroe/CredentialsTest.java @@ -1,6 +1,5 @@ package org.tron.keystroe; -import lombok.var; import org.junit.Assert; import org.junit.Test; import org.mockito.Mockito; @@ -11,24 +10,24 @@ public class CredentialsTest { @Test public void test_equality() { - var aObject = new Object(); - var si = Mockito.mock(SignInterface.class); - var si2 = Mockito.mock(SignInterface.class); - var si3 = Mockito.mock(SignInterface.class); - var address = "TQhZ7W1RudxFdzJMw6FvMnujPxrS6sFfmj".getBytes(); - var address2 = "TNCmcTdyrYKMtmE1KU2itzeCX76jGm5Not".getBytes(); + Object aObject = new Object(); + SignInterface si = Mockito.mock(SignInterface.class); + SignInterface si2 = Mockito.mock(SignInterface.class); + SignInterface si3 = Mockito.mock(SignInterface.class); + byte[] address = "TQhZ7W1RudxFdzJMw6FvMnujPxrS6sFfmj".getBytes(); + byte[] address2 = "TNCmcTdyrYKMtmE1KU2itzeCX76jGm5Not".getBytes(); Mockito.when(si.getAddress()).thenReturn(address); Mockito.when(si2.getAddress()).thenReturn(address); Mockito.when(si3.getAddress()).thenReturn(address2); - var aCredential = Credentials.create(si); + Credentials aCredential = Credentials.create(si); Assert.assertFalse(aObject.equals(aCredential)); Assert.assertFalse(aCredential.equals(aObject)); Assert.assertFalse(aCredential.equals(null)); - var anotherCredential = Credentials.create(si); - Assert.assertTrue(aCredential.equals(anotherCredential)); - var aCredential2 = Credentials.create(si2); + Credentials anotherCredential = Credentials.create(si); Assert.assertTrue(aCredential.equals(anotherCredential)); - var aCredential3 = Credentials.create(si3); + Credentials aCredential2 = Credentials.create(si2); + Assert.assertTrue(aCredential.equals(anotherCredential)); + Credentials aCredential3 = Credentials.create(si3); Assert.assertFalse(aCredential.equals(aCredential3)); } } diff --git a/gradle/jdk17/java-tron.vmoptions b/gradle/jdk17/java-tron.vmoptions new file mode 100644 index 00000000000..91accd05016 --- /dev/null +++ b/gradle/jdk17/java-tron.vmoptions @@ -0,0 +1,8 @@ +-XX:+UseZGC +-Xlog:gc*:file=gc.log:time,uptime,level,tags:filecount=50,filesize=100M +-XX:ReservedCodeCacheSize=256m +-XX:+UseCodeCacheFlushing +-XX:MetaspaceSize=256m +-XX:MaxMetaspaceSize=512m +-XX:MaxDirectMemorySize=1g +-XX:+HeapDumpOnOutOfMemoryError \ No newline at end of file diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index 5b80b8d706e..53d4958f1e5 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -521,9 +521,16 @@ + + + + + + + @@ -894,9 +901,16 @@ + + + + + + + @@ -1074,6 +1088,14 @@ + + + + + + + + @@ -1106,6 +1128,14 @@ + + + + + + + + @@ -1507,28 +1537,28 @@ - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + @@ -2058,12 +2088,12 @@ - - - + + + - - + + @@ -2090,6 +2120,14 @@ + + + + + + + + diff --git a/platform/build.gradle b/platform/build.gradle new file mode 100644 index 00000000000..a94aad3cf17 --- /dev/null +++ b/platform/build.gradle @@ -0,0 +1,17 @@ +description = "platform – a distributed consensus arithmetic for blockchain." + +sourceSets { + main { + java.srcDirs = rootProject.archInfo.sourceSets.main.java.srcDirs + } + test { + java.srcDirs = rootProject.archInfo.sourceSets.test.java.srcDirs + } +} + +dependencies { + api group: 'org.fusesource.leveldbjni', name: 'leveldbjni-all', version: '1.8' + api group: 'org.rocksdb', name: 'rocksdbjni', version: "${rootProject.archInfo.requires.RocksdbVersion}" + api group: 'commons-io', name: 'commons-io', version: '2.18.0' + api 'io.github.tronprotocol:zksnark-java-sdk:1.0.0' exclude(group: 'commons-io', module: 'commons-io') +} diff --git a/platform/src/main/java/arm/org/tron/common/math/MathWrapper.java b/platform/src/main/java/arm/org/tron/common/math/MathWrapper.java new file mode 100644 index 00000000000..12395dffcea --- /dev/null +++ b/platform/src/main/java/arm/org/tron/common/math/MathWrapper.java @@ -0,0 +1,254 @@ +package org.tron.common.math; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +/** + * This class is deprecated and should not be used in new code, + * for cross-platform consistency, please use {@link StrictMathWrapper} instead, + * especially for floating-point calculations. + */ +@Deprecated +public class MathWrapper { + + private static final Map powData = Collections.synchronizedMap(new HashMap<>()); + private static final String EXPONENT = "3f40624dd2f1a9fc"; // 1/2000 = 0.0005 + + public static double pow(double a, double b) { + double strictResult = StrictMath.pow(a, b); + return powData.getOrDefault(new PowData(a, b), strictResult); + } + + /** + * This static block is used to initialize the data map. + */ + static { + // init main-net pow data start + addPowData("3ff0192278704be3", EXPONENT, "3ff000033518c576"); // 4137160(block) + addPowData("3ff000002fc6a33f", EXPONENT, "3ff0000000061d86"); // 4065476 + addPowData("3ff00314b1e73ecf", EXPONENT, "3ff0000064ea3ef8"); // 4071538 + addPowData("3ff0068cd52978ae", EXPONENT, "3ff00000d676966c"); // 4109544 + addPowData("3ff0032fda05447d", EXPONENT, "3ff0000068636fe0"); // 4123826 + addPowData("3ff00051c09cc796", EXPONENT, "3ff000000a76c20e"); // 4166806 + addPowData("3ff00bef8115b65d", EXPONENT, "3ff0000186893de0"); // 4225778 + addPowData("3ff009b0b2616930", EXPONENT, "3ff000013d27849e"); // 4251796 + addPowData("3ff00364ba163146", EXPONENT, "3ff000006f26a9dc"); // 4257157 + addPowData("3ff019be4095d6ae", EXPONENT, "3ff0000348e9f02a"); // 4260583 + addPowData("3ff0123e52985644", EXPONENT, "3ff0000254797fd0"); // 4367125 + addPowData("3ff0126d052860e2", EXPONENT, "3ff000025a6cde26"); // 4402197 + addPowData("3ff0001632cccf1b", EXPONENT, "3ff0000002d76406"); // 4405788 + addPowData("3ff0000965922b01", EXPONENT, "3ff000000133e966"); // 4490332 + addPowData("3ff00005c7692d61", EXPONENT, "3ff0000000bd5d34"); // 4499056 + addPowData("3ff015cba20ec276", EXPONENT, "3ff00002c84cef0e"); // 4518035 + addPowData("3ff00002f453d343", EXPONENT, "3ff000000060cf4e"); // 4533215 + addPowData("3ff006ea73f88946", EXPONENT, "3ff00000e26d4ea2"); // 4647814 + addPowData("3ff00a3632db72be", EXPONENT, "3ff000014e3382a6"); // 4766695 + addPowData("3ff000c0e8df0274", EXPONENT, "3ff0000018b0aeb2"); // 4771494 + addPowData("3ff00015c8f06afe", EXPONENT, "3ff0000002c9d73e"); // 4793587 + addPowData("3ff00068def18101", EXPONENT, "3ff000000d6c3cac"); // 4801947 + addPowData("3ff01349f3ac164b", EXPONENT, "3ff000027693328a"); // 4916843 + addPowData("3ff00e86a7859088", EXPONENT, "3ff00001db256a52"); // 4924111 + addPowData("3ff00000c2a51ab7", EXPONENT, "3ff000000018ea20"); // 5098864 + addPowData("3ff020fb74e9f170", EXPONENT, "3ff00004346fbfa2"); // 5133963 + addPowData("3ff00001ce277ce7", EXPONENT, "3ff00000003b27dc"); // 5139389 + addPowData("3ff005468a327822", EXPONENT, "3ff00000acc20750"); // 5151258 + addPowData("3ff00006666f30ff", EXPONENT, "3ff0000000d1b80e"); // 5185021 + addPowData("3ff000045a0b2035", EXPONENT, "3ff00000008e98e6"); // 5295829 + addPowData("3ff00e00380e10d7", EXPONENT, "3ff00001c9ff83c8"); // 5380897 + addPowData("3ff00c15de2b0d5e", EXPONENT, "3ff000018b6eaab6"); // 5400886 + addPowData("3ff00042afe6956a", EXPONENT, "3ff0000008892244"); // 5864127 + addPowData("3ff0005b7357c2d4", EXPONENT, "3ff000000bb48572"); // 6167339 + addPowData("3ff00033d5ab51c8", EXPONENT, "3ff0000006a279c8"); // 6240974 + addPowData("3ff0000046d74585", EXPONENT, "3ff0000000091150"); // 6279093 + addPowData("3ff0010403f34767", EXPONENT, "3ff0000021472146"); // 6428736 + addPowData("3ff00496fe59bc98", EXPONENT, "3ff000009650a4ca"); // 6432355,6493373 + addPowData("3ff0012e43815868", EXPONENT, "3ff0000026af266e"); // 6555029 + addPowData("3ff00021f6080e3c", EXPONENT, "3ff000000458d16a"); // 7092933 + addPowData("3ff000489c0f28bd", EXPONENT, "3ff00000094b3072"); // 7112412 + addPowData("3ff00009d3df2e9c", EXPONENT, "3ff00000014207b4"); // 7675535 + addPowData("3ff000def05fa9c8", EXPONENT, "3ff000001c887cdc"); // 7860324 + addPowData("3ff0013bca543227", EXPONENT, "3ff00000286a42d2"); // 8292427 + addPowData("3ff0021a2f14a0ee", EXPONENT, "3ff0000044deb040"); // 8517311 + addPowData("3ff0002cc166be3c", EXPONENT, "3ff0000005ba841e"); // 8763101 + addPowData("3ff0000cc84e613f", EXPONENT, "3ff0000001a2da46"); // 9269124 + addPowData("3ff000057b83c83f", EXPONENT, "3ff0000000b3a640"); // 9631452 + // init main-net pow data end + // add pow data + } + + private static void addPowData(String a, String b, String ret) { + powData.put(new PowData(hexToDouble(a), hexToDouble(b)), hexToDouble(ret)); + } + + private static double hexToDouble(String input) { + // Convert the hex string to a long + long hexAsLong = Long.parseLong(input, 16); + // and then convert the long to a double + return Double.longBitsToDouble(hexAsLong); + } + + private static class PowData { + final double a; + final double b; + + public PowData(double a, double b) { + this.a = a; + this.b = b; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + PowData powData = (PowData) o; + return Double.compare(powData.a, a) == 0 && Double.compare(powData.b, b) == 0; + } + + @Override + public int hashCode() { + return Objects.hash(a, b); + } + } + + /** + * *** methods are same as {@link java.lang.Math} methods, guaranteed by the call start *** + */ + + /** + * finally calls {@link java.lang.Math#addExact(long, long)} + */ + + public static long addExact(long x, long y) { + return StrictMath.addExact(x, y); + } + + /** + * finally calls {@link java.lang.Math#addExact(int, int)} + */ + + public static int addExact(int x, int y) { + return StrictMath.addExact(x, y); + } + + /** + * finally calls {@link java.lang.Math#subtractExact(long, long)} + */ + + public static long subtractExact(long x, long y) { + return StrictMath.subtractExact(x, y); + } + + /** + * finally calls {@link java.lang.Math#floorMod(long, long)} + */ + public static long multiplyExact(long x, long y) { + return StrictMath.multiplyExact(x, y); + } + + public static long multiplyExact(long x, int y) { + return multiplyExact(x, (long) y); + } + + public static int multiplyExact(int x, int y) { + return StrictMath.multiplyExact(x, y); + } + + /** + * finally calls {@link java.lang.Math#floorDiv(long, long)} + */ + public static long floorDiv(long x, long y) { + return StrictMath.floorDiv(x, y); + } + + public static long floorDiv(long x, int y) { + return floorDiv(x, (long) y); + } + + /** + * finally calls {@link java.lang.Math#min(int, int)} + */ + public static int min(int a, int b) { + return StrictMath.min(a, b); + } + + /** + * finally calls {@link java.lang.Math#min(long, long)} + */ + public static long min(long a, long b) { + return StrictMath.min(a, b); + } + + /** + * finally calls {@link java.lang.Math#max(int, int)} + */ + public static int max(int a, int b) { + return StrictMath.max(a, b); + } + + /** + * finally calls {@link java.lang.Math#max(long, long)} + */ + public static long max(long a, long b) { + return StrictMath.max(a, b); + } + + /** + * finally calls {@link java.lang.Math#round(float)} + */ + public static int round(float a) { + return StrictMath.round(a); + } + + /** + * finally calls {@link java.lang.Math#round(double)} + */ + public static long round(double a) { + return StrictMath.round(a); + } + + /** + * finally calls {@link java.lang.Math#signum(double)} + */ + public static double signum(double d) { + return StrictMath.signum(d); + } + + /** + * finally calls {@link java.lang.Math#signum(float)} + */ + public static long abs(long a) { + return StrictMath.abs(a); + } + + /** + * *** methods are same as {@link java.lang.Math} methods, guaranteed by the call end *** + */ + + /** + * *** methods are same as {@link java.lang.Math} methods by mathematical algorithms*** + * / + + + /** + * mathematical integer: ceil(i) = floor(i) = i + * @return the smallest (closest to negative infinity) double value that is greater + * than or equal to the argument and is equal to a mathematical integer. + */ + public static double ceil(double a) { + return StrictMath.ceil(a); + } + + /** + * *** methods are no matters *** + */ + public static double random() { + return StrictMath.random(); + } + +} diff --git a/platform/src/main/java/arm/org/tron/common/utils/MarketOrderPriceComparatorForRocksDB.java b/platform/src/main/java/arm/org/tron/common/utils/MarketOrderPriceComparatorForRocksDB.java new file mode 100644 index 00000000000..26c246faf0e --- /dev/null +++ b/platform/src/main/java/arm/org/tron/common/utils/MarketOrderPriceComparatorForRocksDB.java @@ -0,0 +1,32 @@ +package org.tron.common.utils; + +import java.nio.ByteBuffer; +import org.rocksdb.AbstractComparator; +import org.rocksdb.ComparatorOptions; + +public class MarketOrderPriceComparatorForRocksDB extends AbstractComparator { + + public MarketOrderPriceComparatorForRocksDB(final ComparatorOptions copt) { + super(copt); + } + + @Override + public String name() { + return "MarketOrderPriceComparator"; + } + + @Override + public int compare(final ByteBuffer a, final ByteBuffer b) { + return MarketComparator.comparePriceKey(convertDataToBytes(a), convertDataToBytes(b)); + } + + /** + * DirectSlice.data().array will throw UnsupportedOperationException. + * */ + public byte[] convertDataToBytes(ByteBuffer buf) { + byte[] bytes = new byte[buf.remaining()]; + buf.get(bytes); + return bytes; + } + +} diff --git a/platform/src/main/java/common/org/tron/common/arch/Arch.java b/platform/src/main/java/common/org/tron/common/arch/Arch.java new file mode 100644 index 00000000000..20744d755a4 --- /dev/null +++ b/platform/src/main/java/common/org/tron/common/arch/Arch.java @@ -0,0 +1,86 @@ +package org.tron.common.arch; + +import lombok.extern.slf4j.Slf4j; + +@Slf4j(topic = "arch") +public final class Arch { + + private Arch() { + } + + public static String withAll() { + final StringBuilder info = new StringBuilder(); + info.append("os.name").append(": ").append(getOsName()).append("\n"); + info.append("os.arch").append(": ").append(getOsArch()).append("\n"); + info.append("bit.model").append(": ").append(getBitModel()).append("\n"); + info.append("java.version").append(": ").append(javaVersion()).append("\n"); + info.append("java.specification.version").append(": ").append(javaSpecificationVersion()) + .append("\n"); + info.append("java.vendor").append(": ").append(javaVendor()).append("\n"); + return info.toString(); + } + + public static String getOsName() { + return System.getProperty("os.name").toLowerCase().trim(); + + } + public static String getOsArch() { + return System.getProperty("os.arch").toLowerCase().trim(); + } + + public static int getBitModel() { + String prop = System.getProperty("sun.arch.data.model"); + if (prop == null) { + prop = System.getProperty("com.ibm.vm.bitmode"); + } + if (prop != null) { + return Integer.parseInt(prop); + } + // GraalVM support, see https://github.com/fusesource/jansi/issues/162 + String arch = System.getProperty("os.arch"); + if (arch.endsWith("64") && "Substrate VM".equals(System.getProperty("java.vm.name"))) { + return 64; + } + return -1; // we don't know... + } + + public static String javaVersion() { + return System.getProperty("java.version").toLowerCase().trim(); + } + + public static String javaSpecificationVersion() { + return System.getProperty("java.specification.version").toLowerCase().trim(); + } + + public static String javaVendor() { + return System.getProperty("java.vendor").toLowerCase().trim(); + } + + public static boolean isArm64() { + String osArch = getOsArch(); + return osArch.contains("arm64") || osArch.contains("aarch64"); + } + + public static boolean isX86() { + return !isArm64(); + } + + public static boolean isJava8() { + return javaSpecificationVersion().equals("1.8"); + } + + public static void throwUnsupportedJavaException() { + if (isX86() && !isJava8()) { + logger.info(withAll()); + throw new UnsupportedOperationException(String.format( + "Java %s is required for %s architecture. Detected version %s", + "1.8 ", getOsArch(), javaSpecificationVersion())); + } + } + + public static void throwUnsupportedArm64Exception() { + if (isArm64()) { + throw new UnsupportedOperationException("unsupported on " + getOsArch() + " architecture"); + } + } +} diff --git a/plugins/src/main/java/org/tron/plugins/utils/MarketUtils.java b/platform/src/main/java/common/org/tron/common/utils/MarketComparator.java similarity index 50% rename from plugins/src/main/java/org/tron/plugins/utils/MarketUtils.java rename to platform/src/main/java/common/org/tron/common/utils/MarketComparator.java index dbd578a59a3..c2742d4d10b 100644 --- a/plugins/src/main/java/org/tron/plugins/utils/MarketUtils.java +++ b/platform/src/main/java/common/org/tron/common/utils/MarketComparator.java @@ -1,71 +1,10 @@ -package org.tron.plugins.utils; +package org.tron.common.utils; import java.math.BigInteger; -import org.tron.plugins.utils.ByteArray; -public class MarketUtils { +public class MarketComparator { - public static final int TOKEN_ID_LENGTH = ByteArray - .fromString(Long.toString(Long.MAX_VALUE)).length; // 19 - - - - /** - * In order to avoid the difference between the data of same key stored and fetched by hashMap and - * levelDB, when creating the price key, we will find the GCD (Greatest Common Divisor) of - * sellTokenQuantity and buyTokenQuantity. - */ - public static byte[] createPairPriceKey(byte[] sellTokenId, byte[] buyTokenId, - long sellTokenQuantity, long buyTokenQuantity) { - - byte[] sellTokenQuantityBytes; - byte[] buyTokenQuantityBytes; - - // cal the GCD - long gcd = findGCD(sellTokenQuantity, buyTokenQuantity); - if (gcd == 0) { - sellTokenQuantityBytes = ByteArray.fromLong(sellTokenQuantity); - buyTokenQuantityBytes = ByteArray.fromLong(buyTokenQuantity); - } else { - sellTokenQuantityBytes = ByteArray.fromLong(sellTokenQuantity / gcd); - buyTokenQuantityBytes = ByteArray.fromLong(buyTokenQuantity / gcd); - } - - return doCreatePairPriceKey(sellTokenId, buyTokenId, - sellTokenQuantityBytes, buyTokenQuantityBytes); - } - - public static long findGCD(long number1, long number2) { - if (number1 == 0 || number2 == 0) { - return 0; - } - return calGCD(number1, number2); - } - - private static long calGCD(long number1, long number2) { - if (number2 == 0) { - return number1; - } - return calGCD(number2, number1 % number2); - } - - - private static byte[] doCreatePairPriceKey(byte[] sellTokenId, byte[] buyTokenId, - byte[] sellTokenQuantity, byte[] buyTokenQuantity) { - byte[] result = new byte[TOKEN_ID_LENGTH + TOKEN_ID_LENGTH - + sellTokenQuantity.length + buyTokenQuantity.length]; - - System.arraycopy(sellTokenId, 0, result, 0, sellTokenId.length); - System.arraycopy(buyTokenId, 0, result, TOKEN_ID_LENGTH, buyTokenId.length); - System.arraycopy(sellTokenQuantity, 0, result, - TOKEN_ID_LENGTH + TOKEN_ID_LENGTH, - sellTokenQuantity.length); - System.arraycopy(buyTokenQuantity, 0, result, - TOKEN_ID_LENGTH + TOKEN_ID_LENGTH + buyTokenQuantity.length, - buyTokenQuantity.length); - - return result; - } + public static final int TOKEN_ID_LENGTH = Long.toString(Long.MAX_VALUE).getBytes().length; // 19 public static int comparePriceKey(byte[] o1, byte[] o2) { @@ -76,7 +15,7 @@ public static int comparePriceKey(byte[] o1, byte[] o2) { System.arraycopy(o1, 0, pair1, 0, TOKEN_ID_LENGTH * 2); System.arraycopy(o2, 0, pair2, 0, TOKEN_ID_LENGTH * 2); - int pairResult = ByteArray.compareUnsigned(pair1, pair2); + int pairResult = compareUnsigned(pair1, pair2); if (pairResult != 0) { return pairResult; } @@ -100,10 +39,10 @@ public static int comparePriceKey(byte[] o1, byte[] o2) { System.arraycopy(o2, TOKEN_ID_LENGTH + TOKEN_ID_LENGTH + longByteNum, getBuyTokenQuantity2, 0, longByteNum); - long sellTokenQuantity1 = ByteArray.toLong(getSellTokenQuantity1); - long buyTokenQuantity1 = ByteArray.toLong(getBuyTokenQuantity1); - long sellTokenQuantity2 = ByteArray.toLong(getSellTokenQuantity2); - long buyTokenQuantity2 = ByteArray.toLong(getBuyTokenQuantity2); + long sellTokenQuantity1 = toLong(getSellTokenQuantity1); + long buyTokenQuantity1 = toLong(getBuyTokenQuantity1); + long sellTokenQuantity2 = toLong(getSellTokenQuantity2); + long buyTokenQuantity2 = toLong(getBuyTokenQuantity2); if ((sellTokenQuantity1 == 0 || buyTokenQuantity1 == 0) && (sellTokenQuantity2 == 0 || buyTokenQuantity2 == 0)) { @@ -145,4 +84,41 @@ public static int comparePrice(long price1SellQuantity, long price1BuyQuantity, return price1BuyQuantityBI.multiply(price2SellQuantityBI) .compareTo(price2BuyQuantityBI.multiply(price1SellQuantityBI)); } + + /** + * copy from org.bouncycastle.util.Arrays.compareUnsigned + */ + private static int compareUnsigned(byte[] a, byte[] b) { + if (a == b) { + return 0; + } + if (a == null) { + return -1; + } + if (b == null) { + return 1; + } + int minLen = StrictMath.min(a.length, b.length); + for (int i = 0; i < minLen; ++i) { + int aVal = a[i] & 0xFF; + int bVal = b[i] & 0xFF; + if (aVal < bVal) { + return -1; + } + if (aVal > bVal) { + return 1; + } + } + if (a.length < b.length) { + return -1; + } + if (a.length > b.length) { + return 1; + } + return 0; + } + + public static long toLong(byte[] b) { + return (b == null || b.length == 0) ? 0 : new BigInteger(1, b).longValue(); + } } diff --git a/chainbase/src/main/java/org/tron/common/utils/MarketOrderPriceComparatorForLevelDB.java b/platform/src/main/java/common/org/tron/common/utils/MarketOrderPriceComparatorForLevelDB.java similarity index 87% rename from chainbase/src/main/java/org/tron/common/utils/MarketOrderPriceComparatorForLevelDB.java rename to platform/src/main/java/common/org/tron/common/utils/MarketOrderPriceComparatorForLevelDB.java index c4c519f68f1..efb7219b211 100644 --- a/chainbase/src/main/java/org/tron/common/utils/MarketOrderPriceComparatorForLevelDB.java +++ b/platform/src/main/java/common/org/tron/common/utils/MarketOrderPriceComparatorForLevelDB.java @@ -1,7 +1,5 @@ package org.tron.common.utils; -import org.tron.core.capsule.utils.MarketUtils; - public class MarketOrderPriceComparatorForLevelDB implements org.iq80.leveldb.DBComparator { @Override @@ -26,7 +24,7 @@ public byte[] findShortSuccessor(byte[] key) { */ @Override public int compare(byte[] o1, byte[] o2) { - return MarketUtils.comparePriceKey(o1, o2); + return MarketComparator.comparePriceKey(o1, o2); } } diff --git a/common/src/main/java/org/tron/common/math/MathWrapper.java b/platform/src/main/java/x86/org/tron/common/math/MathWrapper.java similarity index 100% rename from common/src/main/java/org/tron/common/math/MathWrapper.java rename to platform/src/main/java/x86/org/tron/common/math/MathWrapper.java diff --git a/chainbase/src/main/java/org/tron/common/utils/MarketOrderPriceComparatorForRockDB.java b/platform/src/main/java/x86/org/tron/common/utils/MarketOrderPriceComparatorForRocksDB.java similarity index 70% rename from chainbase/src/main/java/org/tron/common/utils/MarketOrderPriceComparatorForRockDB.java rename to platform/src/main/java/x86/org/tron/common/utils/MarketOrderPriceComparatorForRocksDB.java index d3812f0f6bc..be406ff658d 100644 --- a/chainbase/src/main/java/org/tron/common/utils/MarketOrderPriceComparatorForRockDB.java +++ b/platform/src/main/java/x86/org/tron/common/utils/MarketOrderPriceComparatorForRocksDB.java @@ -3,11 +3,10 @@ import org.rocksdb.ComparatorOptions; import org.rocksdb.DirectSlice; import org.rocksdb.util.DirectBytewiseComparator; -import org.tron.core.capsule.utils.MarketUtils; -public class MarketOrderPriceComparatorForRockDB extends DirectBytewiseComparator { +public class MarketOrderPriceComparatorForRocksDB extends DirectBytewiseComparator { - public MarketOrderPriceComparatorForRockDB(final ComparatorOptions copt) { + public MarketOrderPriceComparatorForRocksDB(final ComparatorOptions copt) { super(copt); } @@ -18,7 +17,7 @@ public String name() { @Override public int compare(final DirectSlice a, final DirectSlice b) { - return MarketUtils.comparePriceKey(convertDataToBytes(a), convertDataToBytes(b)); + return MarketComparator.comparePriceKey(convertDataToBytes(a), convertDataToBytes(b)); } /** diff --git a/plugins/README.md b/plugins/README.md index 0db6f2e6143..db25811882f 100644 --- a/plugins/README.md +++ b/plugins/README.md @@ -2,7 +2,7 @@ This package contains a set of tools for TRON, the followings are the documentation for each tool. -## DB Archive +## DB Archive(Requires x86 + LevelDB) DB archive provides the ability to reformat the manifest according to the current `database`, parameters are compatible with the previous `ArchiveManifest`. @@ -26,7 +26,7 @@ DB archive provides the ability to reformat the manifest according to the curren ``` -## DB Convert +## DB Convert(Requires x86 + LevelDB) DB convert provides a helper which can convert LevelDB data to RocksDB data, parameters are compatible with previous `DBConvert`. @@ -34,15 +34,13 @@ DB convert provides a helper which can convert LevelDB data to RocksDB data, par - ``: Input path for leveldb, default: output-directory/database. - ``: Output path for rocksdb, default: output-directory-dst/database. -- `--safe`: In safe mode, read data from leveldb then put into rocksdb, it's a very time-consuming procedure. If not, just change engine.properties from leveldb to rocksdb, rocksdb - is compatible with leveldb for the current version. This may not be the case in the future, default: false. - `-h | --help`: Provide the help info. ### Examples: ```shell script # full command - java -jar Toolkit.jar db convert [-h] [--safe] + java -jar Toolkit.jar db convert [-h] # examples java -jar Toolkit.jar db convert output-directory/database /tmp/database ``` @@ -66,7 +64,7 @@ DB copy provides a helper which can copy LevelDB or RocksDB data quickly on the java -jar Toolkit.jar db cp output-directory/database /tmp/databse ``` -## DB Lite +## DB Lite(LevelDB unavailable on ARM) DB lite provides lite database, parameters are compatible with previous `LiteFullNodeTool`. @@ -134,7 +132,7 @@ Execute move command. java -jar Toolkit.jar db mv -c main_net_config.conf -d /data/tron/output-directory ``` -## DB Root +## DB Root(LevelDB unavailable on ARM) DB root provides a helper which can compute merkle root for tiny db. diff --git a/plugins/build.gradle b/plugins/build.gradle index 01afaa01708..40d68e19d2f 100644 --- a/plugins/build.gradle +++ b/plugins/build.gradle @@ -20,6 +20,15 @@ configurations.getByName('checkstyleConfig') { transitive = false } +sourceSets { + main { + java.srcDirs = rootProject.archInfo.sourceSets.main.java.srcDirs + } + test { + java.srcDirs = rootProject.archInfo.sourceSets.test.java.srcDirs + } +} + dependencies { //local libraries implementation fileTree(dir: 'libs', include: '*.jar') @@ -29,9 +38,18 @@ dependencies { implementation group: 'com.typesafe', name: 'config', version: '1.3.2' implementation group: 'me.tongfei', name: 'progressbar', version: '0.9.3' implementation group: 'org.bouncycastle', name: 'bcprov-jdk15on', version: '1.69' - implementation group: 'org.rocksdb', name: 'rocksdbjni', version: '5.15.10' - implementation 'io.github.tronprotocol:leveldbjni-all:1.18.2' - implementation 'io.github.tronprotocol:leveldb:1.18.2' + if (rootProject.archInfo.isArm64) { + testRuntimeOnly group: 'org.fusesource.hawtjni', name: 'hawtjni-runtime', version: '1.18' // for test + implementation project(":platform") + } else { + implementation project(":platform"), { + exclude(group: 'org.fusesource.leveldbjni', module: 'leveldbjni-all') + exclude(group: 'io.github.tronprotocol', module: 'zksnark-java-sdk') + exclude(group: 'commons-io', module: 'commons-io') + } + implementation 'io.github.tronprotocol:leveldbjni-all:1.18.2' + implementation 'io.github.tronprotocol:leveldb:1.18.2' + } implementation project(":protocol") } @@ -76,6 +94,17 @@ test { destinationFile = file("../framework/build/jacoco/jacocoTest1.exec") classDumpDir = file("$buildDir/jacoco/classpathdumps") } + + if (rootProject.archInfo.isArm64) { + exclude 'org/tron/plugins/leveldb/**' + filter { + excludeTestsMatching '*.*leveldb*' + excludeTestsMatching '*.*Leveldb*' + excludeTestsMatching '*.*LevelDB*' + excludeTestsMatching '*.*LevelDb*' + excludeTestsMatching '*.*Archive*' + } + } } jacocoTestReport { @@ -94,7 +123,7 @@ def binaryRelease(taskName, jarName, mainClass) { from(sourceSets.main.output) { include "/**" } - dependsOn project(':protocol').jar // explicit_dependency + dependsOn (project(':protocol').jar, project(':platform').jar) // explicit_dependency from { configurations.runtimeClasspath.collect { // https://docs.gradle.org/current/userguide/upgrading_version_6.html#changes_6.3 it.isDirectory() ? it : zipTree(it) @@ -127,7 +156,7 @@ def createScript(project, mainClass, name) { } } } -applicationDistribution.from("../gradle/java-tron.vmoptions") { +applicationDistribution.from(rootProject.archInfo.VMOptions) { into "bin" } createScript(project, 'org.tron.plugins.ArchiveManifest', 'ArchiveManifest') diff --git a/plugins/src/main/java/arm/org/tron/plugins/ArchiveManifest.java b/plugins/src/main/java/arm/org/tron/plugins/ArchiveManifest.java new file mode 100644 index 00000000000..b7848cf4c6f --- /dev/null +++ b/plugins/src/main/java/arm/org/tron/plugins/ArchiveManifest.java @@ -0,0 +1,29 @@ +package org.tron.plugins; + +import lombok.extern.slf4j.Slf4j; +import org.tron.common.arch.Arch; + +/** + * ARM architecture only supports RocksDB, + * which does not require manifest rebuilding (manifest rebuilding is a LevelDB-only feature). + * This command is not supported but retained for compatibility. + **/ +@Slf4j(topic = "archive") +public class ArchiveManifest { + + public static void main(String[] args) { + int exitCode = run(args); + System.exit(exitCode); + } + + public static int run(String[] args) { + String tips = String.format( + "%s architecture only supports RocksDB, which does not require manifest rebuilding " + + "(manifest rebuilding is a LevelDB-only feature).", + Arch.getOsArch()); + System.out.println(tips); + logger.warn(tips); + return 0; + } + +} diff --git a/plugins/src/main/java/arm/org/tron/plugins/DbArchive.java b/plugins/src/main/java/arm/org/tron/plugins/DbArchive.java new file mode 100644 index 00000000000..03c52f33f02 --- /dev/null +++ b/plugins/src/main/java/arm/org/tron/plugins/DbArchive.java @@ -0,0 +1,50 @@ +package org.tron.plugins; + +import java.util.concurrent.Callable; +import lombok.extern.slf4j.Slf4j; +import org.tron.common.arch.Arch; +import picocli.CommandLine; +import picocli.CommandLine.Option; + +/** + * ARM architecture only supports RocksDB, + * which does not require manifest rebuilding (manifest rebuilding is a LevelDB-only feature). + * This command is not supported but retained for compatibility. + **/ +@Slf4j(topic = "archive") +@CommandLine.Command(name = "archive", description = "A helper to rewrite leveldb manifest.") +public class DbArchive implements Callable { + + @CommandLine.Spec + CommandLine.Model.CommandSpec spec; + @Option(names = {"-d", "--database-directory"}, + defaultValue = "output-directory/database", + description = "java-tron database directory. Default: ${DEFAULT-VALUE}") + private String databaseDirectory; + + @Option(names = {"-b", "--batch-size"}, + defaultValue = "80000", + description = "deal manifest batch size. Default: ${DEFAULT-VALUE}") + private int maxBatchSize; + + @Option(names = {"-m", "--manifest-size"}, + defaultValue = "0", + description = "manifest min size(M) to archive. Default: ${DEFAULT-VALUE}") + private int maxManifestSize; + + @Option(names = {"-h", "--help"}) + private boolean help; + + + @Override + public Integer call() throws Exception { + String tips = String.format( + "%s architecture only supports RocksDB, which does not require manifest rebuilding " + + "(manifest rebuilding is a LevelDB-only feature).", + Arch.getOsArch()); + spec.commandLine().getErr().println(spec.commandLine().getColorScheme().errorText(tips)); + logger.warn(tips); + return 0; + } + +} diff --git a/plugins/src/main/java/org/tron/plugins/Db.java b/plugins/src/main/java/common/org/tron/plugins/Db.java similarity index 100% rename from plugins/src/main/java/org/tron/plugins/Db.java rename to plugins/src/main/java/common/org/tron/plugins/Db.java diff --git a/plugins/src/main/java/org/tron/plugins/DbConvert.java b/plugins/src/main/java/common/org/tron/plugins/DbConvert.java similarity index 91% rename from plugins/src/main/java/org/tron/plugins/DbConvert.java rename to plugins/src/main/java/common/org/tron/plugins/DbConvert.java index a75b235bbcf..37ea6bdeca4 100644 --- a/plugins/src/main/java/org/tron/plugins/DbConvert.java +++ b/plugins/src/main/java/common/org/tron/plugins/DbConvert.java @@ -20,6 +20,7 @@ import org.rocksdb.RocksDBException; import org.rocksdb.RocksIterator; import org.rocksdb.Status; +import org.tron.common.arch.Arch; import org.tron.plugins.utils.DBUtils; import org.tron.plugins.utils.FileUtils; import picocli.CommandLine; @@ -49,20 +50,19 @@ public class DbConvert implements Callable { description = "Output path for rocksdb. Default: ${DEFAULT-VALUE}") private File dest; - @CommandLine.Option(names = {"--safe"}, - description = "In safe mode, read data from leveldb then put rocksdb." - + "If not, just change engine.properties from leveldb to rocksdb," - + "rocksdb is compatible with leveldb for current version." - + "This may not be the case in the future." - + "Default: ${DEFAULT-VALUE}") - private boolean safe; - @CommandLine.Option(names = {"-h", "--help"}) private boolean help; @Override public Integer call() throws Exception { + if (Arch.isArm64()) { + String tips = String.format("This command is not supported on %s architecture.", + Arch.getOsArch()); + spec.commandLine().getErr().println(spec.commandLine().getColorScheme().errorText(tips)); + logger.error(tips); + return 0; + } if (help) { spec.commandLine().usage(System.out); return 0; @@ -95,12 +95,12 @@ public Integer call() throws Exception { final long time = System.currentTimeMillis(); List services = new ArrayList<>(); files.forEach(f -> services.add( - new DbConverter(src.getPath(), dest.getPath(), f.getName(), safe))); + new DbConverter(src.getPath(), dest.getPath(), f.getName()))); cpList.forEach(f -> services.add( new DbConverter( Paths.get(src.getPath(), DBUtils.CHECKPOINT_DB_V2).toString(), Paths.get(dest.getPath(), DBUtils.CHECKPOINT_DB_V2).toString(), - f.getName(), safe))); + f.getName()))); List fails = ProgressBar.wrap(services.stream(), "convert task").parallel().map( dbConverter -> { try { @@ -140,15 +140,12 @@ static class DbConverter implements Converter { private long srcDbValueSum = 0L; private long dstDbValueSum = 0L; - private boolean safe; - - public DbConverter(String srcDir, String dstDir, String name, boolean safe) { + public DbConverter(String srcDir, String dstDir, String name) { this.srcDir = srcDir; this.dstDir = dstDir; this.dbName = name; this.srcDbPath = Paths.get(this.srcDir, name); this.dstDbPath = Paths.get(this.dstDir, name); - this.safe = safe; } @Override @@ -178,23 +175,15 @@ public boolean doConvert() throws Exception { FileUtils.createDirIfNotExists(dstDir); logger.info("Convert database {} start", this.dbName); - if (safe) { - convertLevelToRocks(); - compact(); - } else { - FileUtils.copyDir(Paths.get(srcDir), Paths.get(dstDir), dbName); - } + convertLevelToRocks(); + compact(); + boolean result = check() && createEngine(dstDbPath.toString()); long etime = System.currentTimeMillis(); if (result) { - if (safe) { - logger.info("Convert database {} successful end with {} key-value {} minutes", - this.dbName, this.srcDbKeyCount, (etime - startTime) / 1000.0 / 60); - } else { - logger.info("Convert database {} successful end {} minutes", - this.dbName, (etime - startTime) / 1000.0 / 60); - } + logger.info("Convert database {} successful end with {} key-value {} minutes", + this.dbName, this.srcDbKeyCount, (etime - startTime) / 1000.0 / 60); } else { logger.info("Convert database {} failure", this.dbName); @@ -310,9 +299,6 @@ private void compact() throws RocksDBException { } private boolean check() throws RocksDBException { - if (!safe) { - return true; - } try ( RocksDB rocks = DBUtils.newRocksDbReadOnly(this.dstDbPath); org.rocksdb.ReadOptions r = new org.rocksdb.ReadOptions().setFillCache(false); diff --git a/plugins/src/main/java/org/tron/plugins/DbCopy.java b/plugins/src/main/java/common/org/tron/plugins/DbCopy.java similarity index 100% rename from plugins/src/main/java/org/tron/plugins/DbCopy.java rename to plugins/src/main/java/common/org/tron/plugins/DbCopy.java diff --git a/plugins/src/main/java/org/tron/plugins/DbLite.java b/plugins/src/main/java/common/org/tron/plugins/DbLite.java similarity index 100% rename from plugins/src/main/java/org/tron/plugins/DbLite.java rename to plugins/src/main/java/common/org/tron/plugins/DbLite.java diff --git a/plugins/src/main/java/org/tron/plugins/DbMove.java b/plugins/src/main/java/common/org/tron/plugins/DbMove.java similarity index 100% rename from plugins/src/main/java/org/tron/plugins/DbMove.java rename to plugins/src/main/java/common/org/tron/plugins/DbMove.java diff --git a/plugins/src/main/java/org/tron/plugins/DbRoot.java b/plugins/src/main/java/common/org/tron/plugins/DbRoot.java similarity index 100% rename from plugins/src/main/java/org/tron/plugins/DbRoot.java rename to plugins/src/main/java/common/org/tron/plugins/DbRoot.java diff --git a/plugins/src/main/java/org/tron/plugins/Toolkit.java b/plugins/src/main/java/common/org/tron/plugins/Toolkit.java similarity index 100% rename from plugins/src/main/java/org/tron/plugins/Toolkit.java rename to plugins/src/main/java/common/org/tron/plugins/Toolkit.java diff --git a/plugins/src/main/java/org/tron/plugins/utils/ByteArray.java b/plugins/src/main/java/common/org/tron/plugins/utils/ByteArray.java similarity index 100% rename from plugins/src/main/java/org/tron/plugins/utils/ByteArray.java rename to plugins/src/main/java/common/org/tron/plugins/utils/ByteArray.java diff --git a/plugins/src/main/java/org/tron/plugins/utils/CryptoUitls.java b/plugins/src/main/java/common/org/tron/plugins/utils/CryptoUitls.java similarity index 100% rename from plugins/src/main/java/org/tron/plugins/utils/CryptoUitls.java rename to plugins/src/main/java/common/org/tron/plugins/utils/CryptoUitls.java diff --git a/plugins/src/main/java/org/tron/plugins/utils/DBUtils.java b/plugins/src/main/java/common/org/tron/plugins/utils/DBUtils.java similarity index 94% rename from plugins/src/main/java/org/tron/plugins/utils/DBUtils.java rename to plugins/src/main/java/common/org/tron/plugins/utils/DBUtils.java index f8559d5dba8..507a8ad798e 100644 --- a/plugins/src/main/java/org/tron/plugins/utils/DBUtils.java +++ b/plugins/src/main/java/common/org/tron/plugins/utils/DBUtils.java @@ -16,8 +16,9 @@ import org.rocksdb.Options; import org.rocksdb.RocksDB; import org.rocksdb.RocksDBException; -import org.tron.plugins.comparator.MarketOrderPriceComparatorForLevelDB; -import org.tron.plugins.comparator.MarketOrderPriceComparatorForRockDB; +import org.tron.common.arch.Arch; +import org.tron.common.utils.MarketOrderPriceComparatorForLevelDB; +import org.tron.common.utils.MarketOrderPriceComparatorForRocksDB; import org.tron.protos.Protocol; public class DBUtils { @@ -65,6 +66,7 @@ static Operator valueOf(byte b) { public static final String ROCKSDB = "ROCKSDB"; public static DB newLevelDb(Path db) throws IOException { + Arch.throwUnsupportedArm64Exception(); File file = db.toFile(); org.iq80.leveldb.Options dbOptions = newDefaultLevelDbOptions(); if (MARKET_PAIR_PRICE_TO_ORDER.equalsIgnoreCase(file.getName())) { @@ -115,7 +117,7 @@ private static Options newDefaultRocksDbOptions(boolean forBulkLoad) { public static RocksDB newRocksDb(Path db) throws RocksDBException { try (Options options = newDefaultRocksDbOptions(false)) { if (MARKET_PAIR_PRICE_TO_ORDER.equalsIgnoreCase(db.getFileName().toString())) { - options.setComparator(new MarketOrderPriceComparatorForRockDB(new ComparatorOptions())); + options.setComparator(new MarketOrderPriceComparatorForRocksDB(new ComparatorOptions())); } return RocksDB.open(options, db.toString()); } @@ -124,7 +126,7 @@ public static RocksDB newRocksDb(Path db) throws RocksDBException { public static RocksDB newRocksDbForBulkLoad(Path db) throws RocksDBException { try (Options options = newDefaultRocksDbOptions(true)) { if (MARKET_PAIR_PRICE_TO_ORDER.equalsIgnoreCase(db.getFileName().toString())) { - options.setComparator(new MarketOrderPriceComparatorForRockDB(new ComparatorOptions())); + options.setComparator(new MarketOrderPriceComparatorForRocksDB(new ComparatorOptions())); } return RocksDB.open(options, db.toString()); } @@ -134,7 +136,7 @@ public static RocksDB newRocksDbForBulkLoad(Path db) throws RocksDBException { public static RocksDB newRocksDbReadOnly(Path db) throws RocksDBException { try (Options options = newDefaultRocksDbOptions(false)) { if (MARKET_PAIR_PRICE_TO_ORDER.equalsIgnoreCase(db.getFileName().toString())) { - options.setComparator(new MarketOrderPriceComparatorForRockDB(new ComparatorOptions())); + options.setComparator(new MarketOrderPriceComparatorForRocksDB(new ComparatorOptions())); } return RocksDB.openReadOnly(options, db.toString()); } diff --git a/plugins/src/main/java/org/tron/plugins/utils/FileUtils.java b/plugins/src/main/java/common/org/tron/plugins/utils/FileUtils.java similarity index 100% rename from plugins/src/main/java/org/tron/plugins/utils/FileUtils.java rename to plugins/src/main/java/common/org/tron/plugins/utils/FileUtils.java diff --git a/plugins/src/main/java/common/org/tron/plugins/utils/MarketUtils.java b/plugins/src/main/java/common/org/tron/plugins/utils/MarketUtils.java new file mode 100644 index 00000000000..9bcfd5e71ce --- /dev/null +++ b/plugins/src/main/java/common/org/tron/plugins/utils/MarketUtils.java @@ -0,0 +1,68 @@ +package org.tron.plugins.utils; + +public class MarketUtils { + + public static final int TOKEN_ID_LENGTH = ByteArray + .fromString(Long.toString(Long.MAX_VALUE)).length; // 19 + + + + /** + * In order to avoid the difference between the data of same key stored and fetched by hashMap and + * levelDB, when creating the price key, we will find the GCD (Greatest Common Divisor) of + * sellTokenQuantity and buyTokenQuantity. + */ + public static byte[] createPairPriceKey(byte[] sellTokenId, byte[] buyTokenId, + long sellTokenQuantity, long buyTokenQuantity) { + + byte[] sellTokenQuantityBytes; + byte[] buyTokenQuantityBytes; + + // cal the GCD + long gcd = findGCD(sellTokenQuantity, buyTokenQuantity); + if (gcd == 0) { + sellTokenQuantityBytes = ByteArray.fromLong(sellTokenQuantity); + buyTokenQuantityBytes = ByteArray.fromLong(buyTokenQuantity); + } else { + sellTokenQuantityBytes = ByteArray.fromLong(sellTokenQuantity / gcd); + buyTokenQuantityBytes = ByteArray.fromLong(buyTokenQuantity / gcd); + } + + return doCreatePairPriceKey(sellTokenId, buyTokenId, + sellTokenQuantityBytes, buyTokenQuantityBytes); + } + + public static long findGCD(long number1, long number2) { + if (number1 == 0 || number2 == 0) { + return 0; + } + return calGCD(number1, number2); + } + + private static long calGCD(long number1, long number2) { + if (number2 == 0) { + return number1; + } + return calGCD(number2, number1 % number2); + } + + + private static byte[] doCreatePairPriceKey(byte[] sellTokenId, byte[] buyTokenId, + byte[] sellTokenQuantity, byte[] buyTokenQuantity) { + byte[] result = new byte[TOKEN_ID_LENGTH + TOKEN_ID_LENGTH + + sellTokenQuantity.length + buyTokenQuantity.length]; + + System.arraycopy(sellTokenId, 0, result, 0, sellTokenId.length); + System.arraycopy(buyTokenId, 0, result, TOKEN_ID_LENGTH, buyTokenId.length); + System.arraycopy(sellTokenQuantity, 0, result, + TOKEN_ID_LENGTH + TOKEN_ID_LENGTH, + sellTokenQuantity.length); + System.arraycopy(buyTokenQuantity, 0, result, + TOKEN_ID_LENGTH + TOKEN_ID_LENGTH + buyTokenQuantity.length, + buyTokenQuantity.length); + + return result; + } + + +} diff --git a/plugins/src/main/java/org/tron/plugins/utils/MerkleRoot.java b/plugins/src/main/java/common/org/tron/plugins/utils/MerkleRoot.java similarity index 100% rename from plugins/src/main/java/org/tron/plugins/utils/MerkleRoot.java rename to plugins/src/main/java/common/org/tron/plugins/utils/MerkleRoot.java diff --git a/plugins/src/main/java/org/tron/plugins/utils/Sha256Hash.java b/plugins/src/main/java/common/org/tron/plugins/utils/Sha256Hash.java similarity index 100% rename from plugins/src/main/java/org/tron/plugins/utils/Sha256Hash.java rename to plugins/src/main/java/common/org/tron/plugins/utils/Sha256Hash.java diff --git a/plugins/src/main/java/org/tron/plugins/utils/db/DBInterface.java b/plugins/src/main/java/common/org/tron/plugins/utils/db/DBInterface.java similarity index 100% rename from plugins/src/main/java/org/tron/plugins/utils/db/DBInterface.java rename to plugins/src/main/java/common/org/tron/plugins/utils/db/DBInterface.java diff --git a/plugins/src/main/java/org/tron/plugins/utils/db/DBIterator.java b/plugins/src/main/java/common/org/tron/plugins/utils/db/DBIterator.java similarity index 100% rename from plugins/src/main/java/org/tron/plugins/utils/db/DBIterator.java rename to plugins/src/main/java/common/org/tron/plugins/utils/db/DBIterator.java diff --git a/plugins/src/main/java/org/tron/plugins/utils/db/DbTool.java b/plugins/src/main/java/common/org/tron/plugins/utils/db/DbTool.java similarity index 85% rename from plugins/src/main/java/org/tron/plugins/utils/db/DbTool.java rename to plugins/src/main/java/common/org/tron/plugins/utils/db/DbTool.java index 429025e8f8b..cf4c69505bc 100644 --- a/plugins/src/main/java/org/tron/plugins/utils/db/DbTool.java +++ b/plugins/src/main/java/common/org/tron/plugins/utils/db/DbTool.java @@ -18,8 +18,8 @@ public class DbTool { private static final String KEY_ENGINE = "ENGINE"; private static final String ENGINE_FILE = "engine.properties"; - private static final String FILE_SEPARATOR = File.separator; private static final String ROCKSDB = "ROCKSDB"; + private static final String LEVELDB = "LEVELDB"; private static final Map dbMap = Maps.newConcurrentMap(); @@ -162,8 +162,7 @@ public static void close() { } private static DbType getDbType(String sourceDir, String dbName) { - String engineFile = String.format("%s%s%s%s%s", sourceDir, FILE_SEPARATOR, - dbName, FILE_SEPARATOR, ENGINE_FILE); + String engineFile = Paths.get(sourceDir, dbName, ENGINE_FILE).toString(); if (!new File(engineFile).exists()) { return DbType.LevelDB; } @@ -175,13 +174,22 @@ private static DbType getDbType(String sourceDir, String dbName) { } } - private static LevelDBImpl openLevelDb(Path db, String name) throws IOException { - return new LevelDBImpl(DBUtils.newLevelDb(db), name); + public static LevelDBImpl openLevelDb(Path db, String name) throws IOException { + LevelDBImpl leveldb = new LevelDBImpl(DBUtils.newLevelDb(db), name); + tryInitEngineFile(db, LEVELDB); + return leveldb; } - private static RocksDBImpl openRocksDb(Path db, String name) throws RocksDBException { - return new RocksDBImpl(DBUtils.newRocksDb(db), name); + public static RocksDBImpl openRocksDb(Path db, String name) throws RocksDBException { + RocksDBImpl rocksdb = new RocksDBImpl(DBUtils.newRocksDb(db), name); + tryInitEngineFile(db, ROCKSDB); + return rocksdb; } - + private static void tryInitEngineFile(Path db, String engine) { + String engineFile = Paths.get(db.toString(), ENGINE_FILE).toString(); + if (FileUtils.createFileIfNotExists(engineFile)) { + FileUtils.writeProperty(engineFile, KEY_ENGINE, engine); + } + } } diff --git a/plugins/src/main/java/org/tron/plugins/utils/db/LevelDBImpl.java b/plugins/src/main/java/common/org/tron/plugins/utils/db/LevelDBImpl.java similarity index 100% rename from plugins/src/main/java/org/tron/plugins/utils/db/LevelDBImpl.java rename to plugins/src/main/java/common/org/tron/plugins/utils/db/LevelDBImpl.java diff --git a/plugins/src/main/java/org/tron/plugins/utils/db/LevelDBIterator.java b/plugins/src/main/java/common/org/tron/plugins/utils/db/LevelDBIterator.java similarity index 100% rename from plugins/src/main/java/org/tron/plugins/utils/db/LevelDBIterator.java rename to plugins/src/main/java/common/org/tron/plugins/utils/db/LevelDBIterator.java diff --git a/plugins/src/main/java/org/tron/plugins/utils/db/RockDBIterator.java b/plugins/src/main/java/common/org/tron/plugins/utils/db/RockDBIterator.java similarity index 100% rename from plugins/src/main/java/org/tron/plugins/utils/db/RockDBIterator.java rename to plugins/src/main/java/common/org/tron/plugins/utils/db/RockDBIterator.java diff --git a/plugins/src/main/java/org/tron/plugins/utils/db/RocksDBImpl.java b/plugins/src/main/java/common/org/tron/plugins/utils/db/RocksDBImpl.java similarity index 100% rename from plugins/src/main/java/org/tron/plugins/utils/db/RocksDBImpl.java rename to plugins/src/main/java/common/org/tron/plugins/utils/db/RocksDBImpl.java diff --git a/plugins/src/main/java/org/tron/plugins/comparator/MarketOrderPriceComparatorForLevelDB.java b/plugins/src/main/java/org/tron/plugins/comparator/MarketOrderPriceComparatorForLevelDB.java deleted file mode 100644 index 0879f770e1f..00000000000 --- a/plugins/src/main/java/org/tron/plugins/comparator/MarketOrderPriceComparatorForLevelDB.java +++ /dev/null @@ -1,28 +0,0 @@ -package org.tron.plugins.comparator; - -import org.iq80.leveldb.DBComparator; -import org.tron.plugins.utils.MarketUtils; - -public class MarketOrderPriceComparatorForLevelDB implements DBComparator { - - @Override - public String name() { - return "MarketOrderPriceComparator"; - } - - @Override - public byte[] findShortestSeparator(byte[] start, byte[] limit) { - return new byte[0]; - } - - @Override - public byte[] findShortSuccessor(byte[] key) { - return new byte[0]; - } - - @Override - public int compare(byte[] o1, byte[] o2) { - return MarketUtils.comparePriceKey(o1, o2); - } - -} \ No newline at end of file diff --git a/plugins/src/main/java/org/tron/plugins/comparator/MarketOrderPriceComparatorForRockDB.java b/plugins/src/main/java/org/tron/plugins/comparator/MarketOrderPriceComparatorForRockDB.java deleted file mode 100644 index cd718bdd2d7..00000000000 --- a/plugins/src/main/java/org/tron/plugins/comparator/MarketOrderPriceComparatorForRockDB.java +++ /dev/null @@ -1,38 +0,0 @@ -package org.tron.plugins.comparator; - -import org.rocksdb.ComparatorOptions; -import org.rocksdb.DirectSlice; -import org.rocksdb.util.DirectBytewiseComparator; -import org.tron.plugins.utils.MarketUtils; - -public class MarketOrderPriceComparatorForRockDB extends DirectBytewiseComparator { - - public MarketOrderPriceComparatorForRockDB(final ComparatorOptions copt) { - super(copt); - } - - @Override - public String name() { - return "MarketOrderPriceComparator"; - } - - @Override - public int compare(final DirectSlice a, final DirectSlice b) { - return MarketUtils.comparePriceKey(convertDataToBytes(a), convertDataToBytes(b)); - } - - /** - * DirectSlice.data().array will throw UnsupportedOperationException. - * */ - public byte[] convertDataToBytes(DirectSlice directSlice) { - int capacity = directSlice.data().capacity(); - byte[] bytes = new byte[capacity]; - - for (int i = 0; i < capacity; i++) { - bytes[i] = directSlice.get(i); - } - - return bytes; - } - -} \ No newline at end of file diff --git a/plugins/src/main/java/org/tron/plugins/ArchiveManifest.java b/plugins/src/main/java/x86/org/tron/plugins/ArchiveManifest.java similarity index 98% rename from plugins/src/main/java/org/tron/plugins/ArchiveManifest.java rename to plugins/src/main/java/x86/org/tron/plugins/ArchiveManifest.java index 4d54df6d299..1d7a91027bf 100644 --- a/plugins/src/main/java/org/tron/plugins/ArchiveManifest.java +++ b/plugins/src/main/java/x86/org/tron/plugins/ArchiveManifest.java @@ -35,6 +35,7 @@ import org.iq80.leveldb.DB; import org.iq80.leveldb.Options; import org.iq80.leveldb.impl.Filename; +import org.tron.plugins.utils.DBUtils; import picocli.CommandLine; import picocli.CommandLine.Option; @@ -183,7 +184,7 @@ public boolean checkManifest(String dir) throws IOException { return false; } logger.info("CurrentName {}/{},size {} kb.", dir, currentName, current.length() / 1024); - if ("market_pair_price_to_order".equalsIgnoreCase(this.name)) { + if (DBUtils.MARKET_PAIR_PRICE_TO_ORDER.equalsIgnoreCase(this.name)) { logger.info("Db {} ignored.", this.name); return false; } diff --git a/plugins/src/main/java/org/tron/plugins/DbArchive.java b/plugins/src/main/java/x86/org/tron/plugins/DbArchive.java similarity index 98% rename from plugins/src/main/java/org/tron/plugins/DbArchive.java rename to plugins/src/main/java/x86/org/tron/plugins/DbArchive.java index e3032731ede..15bb281babf 100644 --- a/plugins/src/main/java/org/tron/plugins/DbArchive.java +++ b/plugins/src/main/java/x86/org/tron/plugins/DbArchive.java @@ -19,6 +19,7 @@ import org.iq80.leveldb.DB; import org.iq80.leveldb.Options; import org.iq80.leveldb.impl.Filename; +import org.tron.plugins.utils.DBUtils; import org.tron.plugins.utils.FileUtils; import picocli.CommandLine; import picocli.CommandLine.Option; @@ -163,7 +164,7 @@ public boolean checkManifest(String dir) throws IOException { return false; } logger.info("CurrentName {}/{},size {} kb.", dir, currentName, current.length() / 1024); - if ("market_pair_price_to_order".equalsIgnoreCase(this.name)) { + if (DBUtils.MARKET_PAIR_PRICE_TO_ORDER.equalsIgnoreCase(this.name)) { logger.info("Db {} ignored.", this.name); return false; } diff --git a/plugins/src/test/java/org/tron/plugins/DbCopyTest.java b/plugins/src/test/java/org/tron/plugins/DbCopyTest.java index 9e488a592aa..7548812b799 100644 --- a/plugins/src/test/java/org/tron/plugins/DbCopyTest.java +++ b/plugins/src/test/java/org/tron/plugins/DbCopyTest.java @@ -4,12 +4,23 @@ import java.util.UUID; import org.junit.Assert; import org.junit.Test; +import org.rocksdb.RocksDBException; +import org.tron.plugins.utils.db.DbTool; import picocli.CommandLine; public class DbCopyTest extends DbTest { @Test - public void testRun() { + public void testRunForLevelDB() throws RocksDBException, IOException { + init(DbTool.DbType.LevelDB); + String[] args = new String[] { "db", "cp", INPUT_DIRECTORY, + genarateTmpDir()}; + Assert.assertEquals(0, cli.execute(args)); + } + + @Test + public void testRunForRocksDB() throws RocksDBException, IOException { + init(DbTool.DbType.RocksDB); String[] args = new String[] { "db", "cp", INPUT_DIRECTORY, genarateTmpDir()}; Assert.assertEquals(0, cli.execute(args)); diff --git a/plugins/src/test/java/org/tron/plugins/DbLiteTest.java b/plugins/src/test/java/org/tron/plugins/DbLiteTest.java index b4c66c9820f..b0c7f3b96be 100644 --- a/plugins/src/test/java/org/tron/plugins/DbLiteTest.java +++ b/plugins/src/test/java/org/tron/plugins/DbLiteTest.java @@ -67,9 +67,10 @@ public void shutdown() throws InterruptedException { context.close(); } - public void init() throws IOException { + public void init(String dbType) throws IOException { dbPath = folder.newFolder().toString(); - Args.setParam(new String[]{"-d", dbPath, "-w", "--p2p-disable", "true"}, + Args.setParam(new String[]{ + "-d", dbPath, "-w", "--p2p-disable", "true", "--storage-db-engine", dbType}, "config-localtest.conf"); // allow account root Args.getInstance().setAllowAccountStateRoot(1); @@ -85,11 +86,11 @@ public void clear() { Args.clearParam(); } - void testTools(String dbType, int checkpointVersion) + public void testTools(String dbType, int checkpointVersion) throws InterruptedException, IOException { logger.info("dbType {}, checkpointVersion {}", dbType, checkpointVersion); dbPath = String.format("%s_%s_%d", dbPath, dbType, System.currentTimeMillis()); - init(); + init(dbType); final String[] argsForSnapshot = new String[]{"-o", "split", "-t", "snapshot", "--fn-data-path", dbPath + File.separator + databaseDir, "--dataset-path", @@ -101,7 +102,6 @@ void testTools(String dbType, int checkpointVersion) final String[] argsForMerge = new String[]{"-o", "merge", "--fn-data-path", dbPath + File.separator + databaseDir, "--dataset-path", dbPath + File.separator + "history"}; - Args.getInstance().getStorage().setDbEngine(dbType); Args.getInstance().getStorage().setCheckpointVersion(checkpointVersion); DbLite.setRecentBlks(3); // start fullNode diff --git a/plugins/src/test/java/org/tron/plugins/DbMoveTest.java b/plugins/src/test/java/org/tron/plugins/DbMoveTest.java index c1bc6b470fc..5b25739f272 100644 --- a/plugins/src/test/java/org/tron/plugins/DbMoveTest.java +++ b/plugins/src/test/java/org/tron/plugins/DbMoveTest.java @@ -1,52 +1,41 @@ package org.tron.plugins; -import static org.iq80.leveldb.impl.Iq80DBFactory.factory; - import java.io.File; import java.io.IOException; import java.net.URL; import java.nio.file.Paths; -import java.util.UUID; import lombok.extern.slf4j.Slf4j; -import org.junit.AfterClass; +import org.junit.After; import org.junit.Assert; -import org.junit.BeforeClass; +import org.junit.Rule; import org.junit.Test; -import org.tron.plugins.utils.FileUtils; +import org.junit.rules.TemporaryFolder; +import org.rocksdb.RocksDBException; +import org.tron.plugins.utils.DBUtils; +import org.tron.plugins.utils.db.DbTool; import picocli.CommandLine; @Slf4j public class DbMoveTest { private static final String OUTPUT_DIRECTORY = "output-directory-toolkit"; - private static final String OUTPUT_DIRECTORY_DATABASE = - Paths.get(OUTPUT_DIRECTORY,"ori","database").toString(); - private static final String ENGINE = "ENGINE"; - private static final String LEVELDB = "LEVELDB"; - private static final String ACCOUNT = "account"; - private static final String TRANS = "trans"; - private static final String MARKET = "market_pair_price_to_order"; - private static final String ENGINE_FILE = "engine.properties"; + @Rule + public TemporaryFolder temporaryFolder = new TemporaryFolder(); - @BeforeClass - public static void init() throws IOException { - File file = new File(OUTPUT_DIRECTORY_DATABASE, ACCOUNT); - factory.open(file, ArchiveManifest.newDefaultLevelDbOptions()).close(); - FileUtils.writeProperty(file + File.separator + ENGINE_FILE, ENGINE, LEVELDB); + private static final String ACCOUNT = "account"; + private static final String TRANS = "trans"; - file = new File(OUTPUT_DIRECTORY_DATABASE, MARKET); - factory.open(file, ArchiveManifest.newDefaultLevelDbOptions()).close(); - FileUtils.writeProperty(file + File.separator + ENGINE_FILE, ENGINE, LEVELDB); - file = new File(OUTPUT_DIRECTORY_DATABASE, TRANS); - factory.open(file, ArchiveManifest.newDefaultLevelDbOptions()).close(); - FileUtils.writeProperty(file + File.separator + ENGINE_FILE, ENGINE, LEVELDB); + private void init(DbTool.DbType dbType, String path) throws IOException, RocksDBException { + DbTool.getDB(path, ACCOUNT, dbType).close(); + DbTool.getDB(path, DBUtils.MARKET_PAIR_PRICE_TO_ORDER, dbType).close(); + DbTool.getDB(path, TRANS, dbType).close(); } - @AfterClass - public static void destroy() { + @After + public void destroy() { deleteDir(new File(OUTPUT_DIRECTORY)); } @@ -74,9 +63,23 @@ private static String getConfig(String config) { } @Test - public void testMv() { + public void testMvForLevelDB() throws RocksDBException, IOException { + File database = temporaryFolder.newFolder("database"); + init(DbTool.DbType.LevelDB, Paths.get(database.getPath()).toString()); + String[] args = new String[] {"db", "mv", "-d", + database.getParent(), "-c", + getConfig("config.conf")}; + CommandLine cli = new CommandLine(new Toolkit()); + Assert.assertEquals(0, cli.execute(args)); + Assert.assertEquals(2, cli.execute(args)); + } + + @Test + public void testMvForRocksDB() throws RocksDBException, IOException { + File database = temporaryFolder.newFolder("database"); + init(DbTool.DbType.RocksDB, Paths.get(database.getPath()).toString()); String[] args = new String[] {"db", "mv", "-d", - Paths.get(OUTPUT_DIRECTORY,"ori").toString(), "-c", + database.getParent(), "-c", getConfig("config.conf")}; CommandLine cli = new CommandLine(new Toolkit()); Assert.assertEquals(0, cli.execute(args)); @@ -84,9 +87,10 @@ public void testMv() { } @Test - public void testDuplicate() { + public void testDuplicate() throws IOException { + File output = temporaryFolder.newFolder(); String[] args = new String[] {"db", "mv", "-d", - Paths.get(OUTPUT_DIRECTORY,"ori").toString(), "-c", + output.getPath(), "-c", getConfig("config-duplicate.conf")}; CommandLine cli = new CommandLine(new Toolkit()); Assert.assertEquals(2, cli.execute(args)); @@ -107,20 +111,19 @@ public void testDicNotExist() { } @Test - public void testConfNotExist() { + public void testConfNotExist() throws IOException { + File output = temporaryFolder.newFolder(); String[] args = new String[] {"db", "mv", "-d", - Paths.get(OUTPUT_DIRECTORY,"ori").toString(), "-c", + output.getPath(), "-c", "config.conf"}; CommandLine cli = new CommandLine(new Toolkit()); Assert.assertEquals(2, cli.execute(args)); } @Test - public void testEmpty() { - File file = new File(OUTPUT_DIRECTORY_DATABASE + File.separator + UUID.randomUUID()); - file.mkdirs(); - file.deleteOnExit(); - String[] args = new String[] {"db", "mv", "-d", file.toString(), "-c", + public void testEmpty() throws IOException { + File output = temporaryFolder.newFolder(); + String[] args = new String[] {"db", "mv", "-d", output.getPath(), "-c", getConfig("config.conf")}; CommandLine cli = new CommandLine(new Toolkit()); diff --git a/plugins/src/test/java/org/tron/plugins/DbRootTest.java b/plugins/src/test/java/org/tron/plugins/DbRootTest.java index b86688f77d5..9d032a43103 100644 --- a/plugins/src/test/java/org/tron/plugins/DbRootTest.java +++ b/plugins/src/test/java/org/tron/plugins/DbRootTest.java @@ -9,10 +9,8 @@ import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.rocksdb.RocksDBException; -import org.tron.plugins.utils.DBUtils; import org.tron.plugins.utils.db.DBInterface; import org.tron.plugins.utils.db.DbTool; -import org.tron.plugins.utils.db.LevelDBImpl; import picocli.CommandLine; @Slf4j @@ -27,8 +25,18 @@ public class DbRootTest { private static final String EMPTY_DB = "empty"; private static final String ERROR_DB = "error"; + + @Test + public void testRootForLevelDB() throws RocksDBException, IOException { + testRoot(DbTool.DbType.LevelDB); + } + @Test - public void testRoot() throws IOException, RocksDBException { + public void testRootForRocksDB() throws RocksDBException, IOException { + testRoot(DbTool.DbType.RocksDB); + } + + public void testRoot(DbTool.DbType dbType) throws IOException, RocksDBException { File file = folder.newFolder(); @@ -36,8 +44,8 @@ public void testRoot() throws IOException, RocksDBException { Assert.assertTrue(database.mkdirs()); - try (DBInterface normal = DbTool.getDB(database.toString(), NORMAL_DB, DbTool.DbType.LevelDB); - DBInterface empty = DbTool.getDB(database.toString(), EMPTY_DB, DbTool.DbType.RocksDB)) { + try (DBInterface normal = DbTool.getDB(database.toString(), NORMAL_DB, dbType); + DBInterface empty = DbTool.getDB(database.toString(), EMPTY_DB, dbType)) { for (int i = 0; i < 10; i++) { normal.put(("" + i).getBytes(), (NORMAL_DB + "-" + i).getBytes()); } @@ -53,8 +61,7 @@ public void testRoot() throws IOException, RocksDBException { "--db", EMPTY_DB}; Assert.assertEquals(0, cli.execute(args)); - try (DBInterface errorDb = new LevelDBImpl( - DBUtils.newLevelDb(Paths.get(database.toString(), ERROR_DB)), ERROR_DB)) { + try (DBInterface errorDb = DbTool.getDB(database.toString(), ERROR_DB, dbType)) { for (int i = 0; i < 10; i++) { errorDb.put(("" + i).getBytes(), (ERROR_DB + "-" + i).getBytes()); } diff --git a/plugins/src/test/java/org/tron/plugins/DbTest.java b/plugins/src/test/java/org/tron/plugins/DbTest.java index 8605fa18d50..9337723e8ff 100644 --- a/plugins/src/test/java/org/tron/plugins/DbTest.java +++ b/plugins/src/test/java/org/tron/plugins/DbTest.java @@ -5,42 +5,43 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Paths; import java.util.UUID; -import org.iq80.leveldb.DB; -import org.junit.Before; import org.junit.Rule; import org.junit.rules.TemporaryFolder; +import org.rocksdb.RocksDBException; import org.tron.plugins.utils.ByteArray; import org.tron.plugins.utils.DBUtils; import org.tron.plugins.utils.MarketUtils; +import org.tron.plugins.utils.db.DBInterface; +import org.tron.plugins.utils.db.DbTool; import picocli.CommandLine; public class DbTest { - String INPUT_DIRECTORY; + public String INPUT_DIRECTORY; private static final String ACCOUNT = "account"; private static final String MARKET = DBUtils.MARKET_PAIR_PRICE_TO_ORDER; - CommandLine cli = new CommandLine(new Toolkit()); + public CommandLine cli = new CommandLine(new Toolkit()); String tmpDir = System.getProperty("java.io.tmpdir"); @Rule public final TemporaryFolder temporaryFolder = new TemporaryFolder(); - @Before - public void init() throws IOException { + public void init(DbTool.DbType dbType) throws IOException, RocksDBException { INPUT_DIRECTORY = temporaryFolder.newFolder().toString(); - initDB(new File(INPUT_DIRECTORY, ACCOUNT)); - initDB(new File(INPUT_DIRECTORY, MARKET)); - initDB(new File(INPUT_DIRECTORY, DBUtils.CHECKPOINT_DB_V2)); + initDB(INPUT_DIRECTORY, ACCOUNT, dbType); + initDB(INPUT_DIRECTORY, MARKET, dbType); + initDB(INPUT_DIRECTORY, DBUtils.CHECKPOINT_DB_V2, dbType); } - private static void initDB(File file) throws IOException { - if (DBUtils.CHECKPOINT_DB_V2.equalsIgnoreCase(file.getName())) { - File dbFile = new File(file, DBUtils.CHECKPOINT_DB_V2); + private static void initDB(String sourceDir, String dbName, DbTool.DbType dbType) + throws IOException, RocksDBException { + if (DBUtils.CHECKPOINT_DB_V2.equalsIgnoreCase(dbName)) { + File dbFile = new File(sourceDir, DBUtils.CHECKPOINT_DB_V2); if (dbFile.mkdirs()) { for (int i = 0; i < 3; i++) { - try (DB db = DBUtils.newLevelDb(Paths.get(dbFile.getPath(), - System.currentTimeMillis() + ""))) { + try (DBInterface db = DbTool.getDB(dbFile.getPath(), + System.currentTimeMillis() + "", dbType)) { for (int j = 0; j < 100; j++) { byte[] bytes = UUID.randomUUID().toString().getBytes(); db.put(bytes, bytes); @@ -50,8 +51,8 @@ private static void initDB(File file) throws IOException { } return; } - try (DB db = DBUtils.newLevelDb(file.toPath())) { - if (MARKET.equalsIgnoreCase(file.getName())) { + try (DBInterface db = DbTool.getDB(sourceDir, dbName, dbType)) { + if (MARKET.equalsIgnoreCase(dbName)) { byte[] sellTokenID1 = ByteArray.fromString("100"); byte[] buyTokenID1 = ByteArray.fromString("200"); byte[] pairPriceKey1 = MarketUtils.createPairPriceKey( diff --git a/plugins/src/test/java/org/tron/plugins/ArchiveManifestTest.java b/plugins/src/test/java/org/tron/plugins/leveldb/ArchiveManifestTest.java similarity index 69% rename from plugins/src/test/java/org/tron/plugins/ArchiveManifestTest.java rename to plugins/src/test/java/org/tron/plugins/leveldb/ArchiveManifestTest.java index 4fd9e537d05..f5880d82e39 100644 --- a/plugins/src/test/java/org/tron/plugins/ArchiveManifestTest.java +++ b/plugins/src/test/java/org/tron/plugins/leveldb/ArchiveManifestTest.java @@ -1,6 +1,4 @@ -package org.tron.plugins; - -import static org.iq80.leveldb.impl.Iq80DBFactory.factory; +package org.tron.plugins.leveldb; import java.io.BufferedReader; import java.io.BufferedWriter; @@ -14,47 +12,41 @@ import java.nio.charset.StandardCharsets; import java.util.Properties; import java.util.UUID; - import lombok.extern.slf4j.Slf4j; -import org.junit.AfterClass; import org.junit.Assert; import org.junit.BeforeClass; +import org.junit.ClassRule; import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.rocksdb.RocksDBException; +import org.tron.plugins.ArchiveManifest; +import org.tron.plugins.utils.DBUtils; +import org.tron.plugins.utils.db.DbTool; @Slf4j public class ArchiveManifestTest { - private static final String OUTPUT_DIRECTORY = "output-directory/database/archiveManifest"; + @ClassRule + public static final TemporaryFolder temporaryFolder = new TemporaryFolder(); + + private static String OUTPUT_DIRECTORY; - private static final String ENGINE = "ENGINE"; - private static final String LEVELDB = "LEVELDB"; - private static final String ROCKSDB = "ROCKSDB"; private static final String ACCOUNT = "account"; private static final String ACCOUNT_ROCKSDB = "account-rocksdb"; - private static final String MARKET = "market_pair_price_to_order"; - private static final String ENGINE_FILE = "engine.properties"; - @BeforeClass - public static void init() throws IOException { - File file = new File(OUTPUT_DIRECTORY,ACCOUNT); - factory.open(file,ArchiveManifest.newDefaultLevelDbOptions()).close(); - writeProperty(file.toString() + File.separator + ENGINE_FILE,ENGINE,LEVELDB); - - file = new File(OUTPUT_DIRECTORY,MARKET); - factory.open(file,ArchiveManifest.newDefaultLevelDbOptions()).close(); - writeProperty(file.toString() + File.separator + ENGINE_FILE,ENGINE,LEVELDB); + public static void init() throws IOException, RocksDBException { + OUTPUT_DIRECTORY = temporaryFolder.newFolder("database").toString(); + File file = new File(OUTPUT_DIRECTORY, ACCOUNT); + DbTool.openLevelDb(file.toPath(),ACCOUNT).close(); - file = new File(OUTPUT_DIRECTORY,ACCOUNT_ROCKSDB); - factory.open(file,ArchiveManifest.newDefaultLevelDbOptions()).close(); - writeProperty(file.toString() + File.separator + ENGINE_FILE,ENGINE,ROCKSDB); + file = new File(OUTPUT_DIRECTORY, DBUtils.MARKET_PAIR_PRICE_TO_ORDER); + DbTool.openLevelDb(file.toPath(), DBUtils.MARKET_PAIR_PRICE_TO_ORDER).close(); - } + file = new File(OUTPUT_DIRECTORY, ACCOUNT_ROCKSDB); + DbTool.openRocksDb(file.toPath(), ACCOUNT_ROCKSDB).close(); - @AfterClass - public static void destroy() { - deleteDir(new File(OUTPUT_DIRECTORY)); } @Test diff --git a/plugins/src/test/java/org/tron/plugins/DbArchiveTest.java b/plugins/src/test/java/org/tron/plugins/leveldb/DbArchiveTest.java similarity index 71% rename from plugins/src/test/java/org/tron/plugins/DbArchiveTest.java rename to plugins/src/test/java/org/tron/plugins/leveldb/DbArchiveTest.java index 10bed418764..69dca01e4f8 100644 --- a/plugins/src/test/java/org/tron/plugins/DbArchiveTest.java +++ b/plugins/src/test/java/org/tron/plugins/leveldb/DbArchiveTest.java @@ -1,6 +1,4 @@ -package org.tron.plugins; - -import static org.iq80.leveldb.impl.Iq80DBFactory.factory; +package org.tron.plugins.leveldb; import java.io.BufferedReader; import java.io.BufferedWriter; @@ -12,48 +10,43 @@ import java.io.OutputStream; import java.io.OutputStreamWriter; import java.nio.charset.StandardCharsets; -import java.nio.file.Paths; import java.util.Properties; import java.util.UUID; import lombok.extern.slf4j.Slf4j; -import org.junit.AfterClass; import org.junit.Assert; import org.junit.BeforeClass; +import org.junit.ClassRule; import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.rocksdb.RocksDBException; +import org.tron.plugins.Toolkit; +import org.tron.plugins.utils.DBUtils; +import org.tron.plugins.utils.db.DbTool; import picocli.CommandLine; @Slf4j public class DbArchiveTest { - private static final String OUTPUT_DIRECTORY = "output-directory/database/dbArchive"; + @ClassRule + public static final TemporaryFolder temporaryFolder = new TemporaryFolder(); + + private static String OUTPUT_DIRECTORY; - private static final String ENGINE = "ENGINE"; - private static final String LEVELDB = "LEVELDB"; - private static final String ROCKSDB = "ROCKSDB"; private static final String ACCOUNT = "account"; private static final String ACCOUNT_ROCKSDB = "account-rocksdb"; - private static final String MARKET = "market_pair_price_to_order"; - private static final String ENGINE_FILE = "engine.properties"; @BeforeClass - public static void init() throws IOException { - File file = new File(OUTPUT_DIRECTORY,ACCOUNT); - factory.open(file,ArchiveManifest.newDefaultLevelDbOptions()).close(); - writeProperty(file.toString() + File.separator + ENGINE_FILE,ENGINE,LEVELDB); + public static void init() throws IOException, RocksDBException { + OUTPUT_DIRECTORY = temporaryFolder.newFolder("database").toString(); + File file = new File(OUTPUT_DIRECTORY, ACCOUNT); + DbTool.openLevelDb(file.toPath(),ACCOUNT).close(); - file = new File(OUTPUT_DIRECTORY,MARKET); - factory.open(file,ArchiveManifest.newDefaultLevelDbOptions()).close(); - writeProperty(file.toString() + File.separator + ENGINE_FILE,ENGINE,LEVELDB); + file = new File(OUTPUT_DIRECTORY, DBUtils.MARKET_PAIR_PRICE_TO_ORDER); + DbTool.openLevelDb(file.toPath(), DBUtils.MARKET_PAIR_PRICE_TO_ORDER).close(); - file = new File(OUTPUT_DIRECTORY,ACCOUNT_ROCKSDB); - factory.open(file,ArchiveManifest.newDefaultLevelDbOptions()).close(); - writeProperty(file.toString() + File.separator + ENGINE_FILE,ENGINE,ROCKSDB); - - } + file = new File(OUTPUT_DIRECTORY, ACCOUNT_ROCKSDB); + DbTool.openRocksDb(file.toPath(), ACCOUNT_ROCKSDB).close(); - @AfterClass - public static void destroy() { - deleteDir(new File(OUTPUT_DIRECTORY)); } @Test diff --git a/plugins/src/test/java/org/tron/plugins/DbConvertTest.java b/plugins/src/test/java/org/tron/plugins/leveldb/DbConvertTest.java similarity index 76% rename from plugins/src/test/java/org/tron/plugins/DbConvertTest.java rename to plugins/src/test/java/org/tron/plugins/leveldb/DbConvertTest.java index 150e47c9f65..d24604f0a0b 100644 --- a/plugins/src/test/java/org/tron/plugins/DbConvertTest.java +++ b/plugins/src/test/java/org/tron/plugins/leveldb/DbConvertTest.java @@ -1,27 +1,25 @@ -package org.tron.plugins; +package org.tron.plugins.leveldb; import java.io.IOException; import java.util.UUID; import org.junit.Assert; import org.junit.Test; +import org.rocksdb.RocksDBException; +import org.tron.plugins.DbTest; +import org.tron.plugins.Toolkit; +import org.tron.plugins.utils.db.DbTool; import picocli.CommandLine; public class DbConvertTest extends DbTest { @Test - public void testRun() throws IOException { + public void testRun() throws IOException, RocksDBException { + init(DbTool.DbType.LevelDB); String[] args = new String[] { "db", "convert", INPUT_DIRECTORY, temporaryFolder.newFolder().toString() }; Assert.assertEquals(0, cli.execute(args)); } - @Test - public void testRunWithSafe() throws IOException { - String[] args = new String[] { "db", "convert", INPUT_DIRECTORY, - temporaryFolder.newFolder().toString(),"--safe" }; - Assert.assertEquals(0, cli.execute(args)); - } - @Test public void testHelp() { String[] args = new String[] {"db", "convert", "-h"}; diff --git a/plugins/src/test/java/org/tron/plugins/DbLiteLevelDbTest.java b/plugins/src/test/java/org/tron/plugins/leveldb/DbLiteLevelDbTest.java similarity index 76% rename from plugins/src/test/java/org/tron/plugins/DbLiteLevelDbTest.java rename to plugins/src/test/java/org/tron/plugins/leveldb/DbLiteLevelDbTest.java index 792f93ad197..7666806e2b5 100644 --- a/plugins/src/test/java/org/tron/plugins/DbLiteLevelDbTest.java +++ b/plugins/src/test/java/org/tron/plugins/leveldb/DbLiteLevelDbTest.java @@ -1,7 +1,8 @@ -package org.tron.plugins; +package org.tron.plugins.leveldb; import java.io.IOException; import org.junit.Test; +import org.tron.plugins.DbLiteTest; public class DbLiteLevelDbTest extends DbLiteTest { diff --git a/plugins/src/test/java/org/tron/plugins/DbLiteLevelDbV2Test.java b/plugins/src/test/java/org/tron/plugins/leveldb/DbLiteLevelDbV2Test.java similarity index 76% rename from plugins/src/test/java/org/tron/plugins/DbLiteLevelDbV2Test.java rename to plugins/src/test/java/org/tron/plugins/leveldb/DbLiteLevelDbV2Test.java index ae48e1d66e9..de32ae29c7c 100644 --- a/plugins/src/test/java/org/tron/plugins/DbLiteLevelDbV2Test.java +++ b/plugins/src/test/java/org/tron/plugins/leveldb/DbLiteLevelDbV2Test.java @@ -1,7 +1,8 @@ -package org.tron.plugins; +package org.tron.plugins.leveldb; import java.io.IOException; import org.junit.Test; +import org.tron.plugins.DbLiteTest; public class DbLiteLevelDbV2Test extends DbLiteTest { diff --git a/plugins/src/test/java/org/tron/plugins/DbLiteRocksDbTest.java b/plugins/src/test/java/org/tron/plugins/rocksdb/DbLiteRocksDbTest.java similarity index 76% rename from plugins/src/test/java/org/tron/plugins/DbLiteRocksDbTest.java rename to plugins/src/test/java/org/tron/plugins/rocksdb/DbLiteRocksDbTest.java index e6910b1103a..2f9c92f9679 100644 --- a/plugins/src/test/java/org/tron/plugins/DbLiteRocksDbTest.java +++ b/plugins/src/test/java/org/tron/plugins/rocksdb/DbLiteRocksDbTest.java @@ -1,7 +1,8 @@ -package org.tron.plugins; +package org.tron.plugins.rocksdb; import java.io.IOException; import org.junit.Test; +import org.tron.plugins.DbLiteTest; public class DbLiteRocksDbTest extends DbLiteTest { diff --git a/plugins/src/test/java/org/tron/plugins/rocksdb/DbLiteRocksDbV2Test.java b/plugins/src/test/java/org/tron/plugins/rocksdb/DbLiteRocksDbV2Test.java new file mode 100644 index 00000000000..ab1067fefc3 --- /dev/null +++ b/plugins/src/test/java/org/tron/plugins/rocksdb/DbLiteRocksDbV2Test.java @@ -0,0 +1,13 @@ +package org.tron.plugins.rocksdb; + +import java.io.IOException; +import org.junit.Test; +import org.tron.plugins.DbLiteTest; + +public class DbLiteRocksDbV2Test extends DbLiteTest { + + @Test + public void testToolsWithRocksDB() throws InterruptedException, IOException { + testTools("ROCKSDB", 2); + } +} diff --git a/plugins/src/test/resources/config.conf b/plugins/src/test/resources/config.conf index 2bfca7dbdd7..77d15d521eb 100644 --- a/plugins/src/test/resources/config.conf +++ b/plugins/src/test/resources/config.conf @@ -1,11 +1,5 @@ storage { - # Directory for storing persistent data - db.engine = "LEVELDB", - db.sync = false, - db.directory = "database", - index.directory = "index", - transHistory.switch = "on", properties = [ { name = "account", diff --git a/settings.gradle b/settings.gradle index eb304444378..af32bfca702 100644 --- a/settings.gradle +++ b/settings.gradle @@ -8,4 +8,5 @@ include 'common' include 'example:actuator-example' include 'crypto' include 'plugins' +include 'platform' diff --git a/start.sh.simple b/start.sh.simple new file mode 100644 index 00000000000..72862d9bdfc --- /dev/null +++ b/start.sh.simple @@ -0,0 +1,195 @@ +#!/bin/bash +############################################################################# +# +# GNU LESSER GENERAL PUBLIC LICENSE +# Version 3, 29 June 2007 +# +# Copyright (C) [2007] [TRON Foundation], Inc. +# Everyone is permitted to copy and distribute verbatim copies +# of this license document, but changing it is not allowed. +# +# +# This version of the GNU Lesser General Public License incorporates +# the terms and conditions of version 3 of the GNU General Public +# License, supplemented by the additional permissions listed below. +# +# You can find java-tron at https://github.com/tronprotocol/java-tron/ +# +############################################################################## +# TRON Full Node Management Simple Script +# +# NOTE: This is a simple and concise script to start and stop the java-tron full node, +# designed for developers to quickly get started and learn. +# It may not be suitable for production environments. +# +# Usage: +# sh start.sh # Start the java-tron FullNode +# sh start.sh -s # Stop the java-tron FullNode +# sh start.sh [options] # Start with additional java-tron options,such as: -c config.conf -d /path_to_data, etc. +# +############################################################################## + + +# adjust JVM start +# Set the minimum and maximum heap size to 9G, adjust as needed +VM_XMS="9G" +# Set the maximum heap size to 9G, adjust as needed +VM_XMX="9G" +# Set the maximum direct memory size to 1G, adjust as needed +VM_MAX_DIRECT_MEMORY_SIZE="1G" +# adjust JVM end + +FULL_NODE_JAR="FullNode.jar" +FULL_START_OPT=() +PID="" +MAX_STOP_TIME=60 +JAVACMD="" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' + +log() { + local level="$1"; shift + local timestamp color="" + timestamp=$(date '+%Y-%m-%d %H:%M:%S') + case "$level" in + INFO) color="$GREEN" ;; + WARN) color="$YELLOW" ;; + ERROR) color="$RED" ;; + esac + printf "%b[%s] [%s]:%b %s\n" "$color" "$timestamp" "$level" "$NC" "$*" | tee -a "${SCRIPT_DIR}/start.log" +} + +info() { log INFO "$@"; } +warn() { log WARN "$@"; } +error() { log ERROR "$@"; } +die() { error "$@"; exit 1; } + +ulimit -n 65535 || warn "Failed to set ulimit -n 65535" + +findJava() { + if [ -n "${JAVA_HOME:-}" ]; then + if [ -x "$JAVA_HOME/jre/sh/java" ]; then + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + [ -x "$JAVACMD" ] || die "JAVA_HOME is invalid: $JAVA_HOME" + else + JAVACMD="java" + which java >/dev/null 2>&1 || die "JAVA_HOME not set and no 'java' in PATH" + fi + "$JAVACMD" -version > /dev/null 2>&1 || die "Java command not working" +} + +checkPid() { + # shellcheck disable=SC2009 + PID=$(ps -ef |grep $FULL_NODE_JAR |grep -v grep |awk '{print $2}') +} + + +stopService() { + checkPid + + if ! kill -0 "$PID" 2>/dev/null; then + info "java-tron is not running." + return 0 + fi + info "Stopping java-tron service (PID: $PID)" + + local count=1 + + while [ -n "$PID" ] && [ $count -le $MAX_STOP_TIME ]; do + kill -TERM "$PID" 2>/dev/null && info "Sent SIGTERM to java-tron (PID: $PID), attempt $count" + sleep 1 + checkPid + count=$((count + 1)) + done + + if [ -n "$PID" ]; then + warn "Forcing kill java-tron (PID: $PID) after $MAX_STOP_TIME seconds" + kill -KILL "$PID" 2>/dev/null + sleep 1 + checkPid + fi + + if [ -n "$PID" ]; then + die "Failed to stop the service (PID: $PID)" + else + info "java-tron stopped" + wait_with_info 2 "Cleaning up..." + fi +} + +startService() { + if [ -n "${FULL_START_OPT[*]}" ]; then + info "Starting java-tron service with options: ${FULL_START_OPT[*]}" + fi + if [ ! -f "$FULL_NODE_JAR" ]; then + die "$FULL_NODE_JAR not found in path $SCRIPT_DIR." + fi + + nohup "$JAVACMD" \ + -Xms"$VM_XMS" -Xmx"$VM_XMX" -XX:ReservedCodeCacheSize=256m \ + -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m \ + -XX:MaxDirectMemorySize="$VM_MAX_DIRECT_MEMORY_SIZE" \ + -Xloggc:gc.log -XX:+PrintGCDetails \ + -XX:+UseG1GC \ + -XX:MaxGCPauseMillis=40 \ + -XX:InitiatingHeapOccupancyPercent=45 \ + -XX:+HeapDumpOnOutOfMemoryError \ + -jar "$FULL_NODE_JAR" "${FULL_START_OPT[@]}" \ + >> start.log 2>&1 & + + + info "Waiting for the service to start..." + wait_with_info 5 "Starting..." + + checkPid + + if [ -n "$PID" ]; then + info "Started java-tron with PID $PID on $HOSTNAME." + else + die "Failed to start java-tron, see start.log or logs/tron.log for details." + fi +} + +wait_with_info() { + local seconds=$1 + local message=$2 + for i in $(seq "$seconds" -1 1); do + info "$message wait ($i) s" + sleep 1 + done +} + + +start() { + checkPid + if [ -n "$PID" ]; then + info "java-tron is already running (PID: $PID), to stop the service: sh start.sh -s" + return + fi + findJava + startService +} + +while [ -n "$1" ]; do + case "$1" in + -s) + stopService + exit 0 + ;; + *) + FULL_START_OPT+=("$@") + break + ;; + esac +done + +start + +exit 0 \ No newline at end of file From d2d49bb9b3c97ec7ef465a8df60454fdd006cc14 Mon Sep 17 00:00:00 2001 From: Federico2014 Date: Thu, 14 Aug 2025 11:53:39 +0800 Subject: [PATCH 25/59] feat(crypto): optimize zksnark config (#6371) * fix(crypto): optimize the zkSNARK and shielded txs config * feat(rpc): remove redundant toggle check * fix(crypto): remove the shielded tx execute toggle * fix(crypto): rename the shielded Transaction api toggle * feat: optimize the test * feat: optimize ZksnarkParams initialization in tests. * remove redundant check * make zk param initialization thread safe * add compatibility with previous configuration --- .../actuator/ShieldedTransferActuator.java | 34 +++--- .../tron/common/zksnark/JLibrustzcash.java | 107 +----------------- .../org/tron/common/zksnark/JLibsodium.java | 35 +----- .../common/parameter/CommonParameter.java | 2 +- .../src/main/java/org/tron/core/Constant.java | 3 + framework/build.gradle | 1 + .../src/main/java/org/tron/core/Wallet.java | 78 +++++-------- .../java/org/tron/core/config/args/Args.java | 25 ++-- .../main/java/org/tron/core/db/Manager.java | 6 +- .../org/tron/core/services/RpcApiService.java | 77 ------------- .../org/tron/core/zen/ZksnarkInitService.java | 44 ++++--- .../java/org/tron/core/ShieldWalletTest.java | 20 ++-- .../tron/core/ShieldedTRC20BuilderTest.java | 14 +-- .../java/org/tron/core/WalletMockTest.java | 6 +- .../ShieldedTransferActuatorTest.java | 4 +- .../tron/core/exception/TronErrorTest.java | 1 - .../filter/HttpApiAccessFilterTest.java | 2 +- .../filter/LiteFnQueryHttpFilterTest.java | 2 +- .../tron/core/zksnark/LibrustzcashTest.java | 12 +- .../tron/core/zksnark/SaplingNoteTest.java | 2 +- .../tron/core/zksnark/SendCoinShieldTest.java | 38 +------ .../core/zksnark/ShieldedReceiveTest.java | 55 ++------- .../src/test/resources/config-localtest.conf | 2 +- 23 files changed, 137 insertions(+), 433 deletions(-) diff --git a/actuator/src/main/java/org/tron/core/actuator/ShieldedTransferActuator.java b/actuator/src/main/java/org/tron/core/actuator/ShieldedTransferActuator.java index 2773dc07d26..338e948f304 100644 --- a/actuator/src/main/java/org/tron/core/actuator/ShieldedTransferActuator.java +++ b/actuator/src/main/java/org/tron/core/actuator/ShieldedTransferActuator.java @@ -170,28 +170,27 @@ private void executeShielded(List spends, List witnessList, long large, int synBlockNum) throws ItemNotFoundException, BadItemException, InvalidProtocolBufferException, ZksnarkException { - if (!getFullNodeAllowShieldedTransaction()) { - throw new ZksnarkException(SHIELDED_ID_NOT_ALLOWED); - } long start = large; long end = large + synBlockNum - 1; @@ -2027,10 +2014,7 @@ private void updateLowWitness(IncrementalMerkleVoucherContainer witness, long bl } } - private void validateInput(OutputPointInfo request) throws BadItemException, ZksnarkException { - if (!getFullNodeAllowShieldedTransaction()) { - throw new ZksnarkException(SHIELDED_ID_NOT_ALLOWED); - } + private void validateInput(OutputPointInfo request) throws BadItemException { if (request.getBlockNum() < 0 || request.getBlockNum() > 1000) { throw new BadItemException("request.BlockNum must be specified with range in [0, 1000]"); } @@ -2056,7 +2040,7 @@ private void validateInput(OutputPointInfo request) throws BadItemException, Zks public IncrementalMerkleVoucherInfo getMerkleTreeVoucherInfo(OutputPointInfo request) throws ItemNotFoundException, BadItemException, InvalidProtocolBufferException, ZksnarkException { - checkFullNodeAllowShieldedTransaction(); + checkAllowShieldedTransactionApi(); validateInput(request); IncrementalMerkleVoucherInfo.Builder result = IncrementalMerkleVoucherInfo.newBuilder(); @@ -2102,9 +2086,7 @@ public IncrementalMerkleVoucherInfo getMerkleTreeVoucherInfo(OutputPointInfo req } public IncrementalMerkleTree getMerkleTreeOfBlock(long blockNum) throws ZksnarkException { - if (!getFullNodeAllowShieldedTransaction()) { - throw new ZksnarkException(SHIELDED_ID_NOT_ALLOWED); - } + checkAllowShieldedTransactionApi(); if (blockNum < 0) { return null; } @@ -2171,7 +2153,7 @@ public ReceiveNote createReceiveNoteRandom(long value) throws ZksnarkException, public TransactionCapsule createShieldedTransaction(PrivateParameters request) throws ContractValidateException, RuntimeException, ZksnarkException, BadItemException { - checkFullNodeAllowShieldedTransaction(); + checkAllowShieldedTransactionApi(); ZenTransactionBuilder builder = new ZenTransactionBuilder(this); @@ -2273,7 +2255,7 @@ public TransactionCapsule createShieldedTransaction(PrivateParameters request) public TransactionCapsule createShieldedTransactionWithoutSpendAuthSig( PrivateParametersWithoutAsk request) throws ContractValidateException, ZksnarkException, BadItemException { - checkFullNodeAllowShieldedTransaction(); + checkAllowShieldedTransactionApi(); ZenTransactionBuilder builder = new ZenTransactionBuilder(this); @@ -2390,7 +2372,7 @@ private void shieldedOutput(List shieldedReceives, public ShieldedAddressInfo getNewShieldedAddress() throws BadItemException, ZksnarkException { - checkFullNodeAllowShieldedTransaction(); + checkAllowShieldedTransactionApi(); ShieldedAddressInfo.Builder addressInfo = ShieldedAddressInfo.newBuilder(); @@ -2423,7 +2405,7 @@ public ShieldedAddressInfo getNewShieldedAddress() throws BadItemException, Zksn } public BytesMessage getSpendingKey() throws ZksnarkException { - checkFullNodeAllowShieldedTransaction(); + checkAllowShieldedTransactionApi(); byte[] sk = SpendingKey.random().getValue(); return BytesMessage.newBuilder().setValue(ByteString.copyFrom(sk)).build(); @@ -2431,7 +2413,7 @@ public BytesMessage getSpendingKey() throws ZksnarkException { public ExpandedSpendingKeyMessage getExpandedSpendingKey(ByteString spendingKey) throws BadItemException, ZksnarkException { - checkFullNodeAllowShieldedTransaction(); + checkAllowShieldedTransactionApi(); if (Objects.isNull(spendingKey)) { throw new BadItemException("spendingKey is null"); @@ -2457,7 +2439,7 @@ public ExpandedSpendingKeyMessage getExpandedSpendingKey(ByteString spendingKey) public BytesMessage getAkFromAsk(ByteString ask) throws BadItemException, ZksnarkException { - checkFullNodeAllowShieldedTransaction(); + checkAllowShieldedTransactionApi(); if (Objects.isNull(ask)) { throw new BadItemException("ask is null"); @@ -2473,7 +2455,7 @@ public BytesMessage getAkFromAsk(ByteString ask) throws public BytesMessage getNkFromNsk(ByteString nsk) throws BadItemException, ZksnarkException { - checkFullNodeAllowShieldedTransaction(); + checkAllowShieldedTransactionApi(); if (Objects.isNull(nsk)) { throw new BadItemException("nsk is null"); @@ -2489,7 +2471,7 @@ public BytesMessage getNkFromNsk(ByteString nsk) throws public IncomingViewingKeyMessage getIncomingViewingKey(byte[] ak, byte[] nk) throws ZksnarkException { - checkFullNodeAllowShieldedTransaction(); + checkAllowShieldedTransactionApi(); byte[] ivk = new byte[32]; // the incoming viewing key JLibrustzcash.librustzcashCrhIvk(new CrhIvkParams(ak, nk, ivk)); @@ -2500,7 +2482,7 @@ public IncomingViewingKeyMessage getIncomingViewingKey(byte[] ak, byte[] nk) } public DiversifierMessage getDiversifier() throws ZksnarkException { - checkFullNodeAllowShieldedTransaction(); + checkAllowShieldedTransactionApi(); byte[] d; while (true) { @@ -2516,7 +2498,7 @@ public DiversifierMessage getDiversifier() throws ZksnarkException { } public BytesMessage getRcm() throws ZksnarkException { - checkFullNodeAllowShieldedTransaction(); + checkAllowShieldedTransactionApi(); byte[] rcm = Note.generateR(); return BytesMessage.newBuilder().setValue(ByteString.copyFrom(rcm)).build(); @@ -2524,7 +2506,7 @@ public BytesMessage getRcm() throws ZksnarkException { public PaymentAddressMessage getPaymentAddress(IncomingViewingKey ivk, DiversifierT d) throws BadItemException, ZksnarkException { - checkFullNodeAllowShieldedTransaction(); + checkAllowShieldedTransactionApi(); if (!JLibrustzcash.librustzcashCheckDiversifier(d.getData())) { throw new BadItemException("d is not valid"); @@ -2548,7 +2530,7 @@ public PaymentAddressMessage getPaymentAddress(IncomingViewingKey ivk, public SpendResult isSpend(NoteParameters noteParameters) throws ZksnarkException, InvalidProtocolBufferException, BadItemException, ItemNotFoundException { - checkFullNodeAllowShieldedTransaction(); + checkAllowShieldedTransactionApi(); GrpcAPI.Note note = noteParameters.getNote(); byte[] ak = noteParameters.getAk().toByteArray(); @@ -2604,7 +2586,7 @@ public SpendResult isSpend(NoteParameters noteParameters) throws public BytesMessage createSpendAuthSig(SpendAuthSigParameters spendAuthSigParameters) throws ZksnarkException { - checkFullNodeAllowShieldedTransaction(); + checkAllowShieldedTransactionApi(); byte[] result = new byte[64]; SpendSigParams spendSigParams = new SpendSigParams( @@ -2618,7 +2600,7 @@ public BytesMessage createSpendAuthSig(SpendAuthSigParameters spendAuthSigParame } public BytesMessage createShieldNullifier(NfParameters nfParameters) throws ZksnarkException { - checkFullNodeAllowShieldedTransaction(); + checkAllowShieldedTransactionApi(); byte[] ak = nfParameters.getAk().toByteArray(); byte[] nk = nfParameters.getNk().toByteArray(); @@ -2654,7 +2636,7 @@ public BytesMessage createShieldNullifier(NfParameters nfParameters) throws Zksn public BytesMessage getShieldTransactionHash(Transaction transaction) throws ContractValidateException, ZksnarkException { - checkFullNodeAllowShieldedTransaction(); + checkAllowShieldedTransactionApi(); List contract = transaction.getRawData().getContractList(); if (contract == null || contract.isEmpty()) { @@ -3359,7 +3341,7 @@ private GrpcAPI.DecryptNotes queryNoteByIvk(long startNum, long endNum, byte[] i */ public GrpcAPI.DecryptNotes scanNoteByIvk(long startNum, long endNum, byte[] ivk) throws BadItemException, ZksnarkException { - checkFullNodeAllowShieldedTransaction(); + checkAllowShieldedTransactionApi(); return queryNoteByIvk(startNum, endNum, ivk); } @@ -3370,7 +3352,7 @@ public GrpcAPI.DecryptNotes scanNoteByIvk(long startNum, long endNum, public GrpcAPI.DecryptNotesMarked scanAndMarkNoteByIvk(long startNum, long endNum, byte[] ivk, byte[] ak, byte[] nk) throws BadItemException, ZksnarkException, InvalidProtocolBufferException, ItemNotFoundException { - checkFullNodeAllowShieldedTransaction(); + checkAllowShieldedTransactionApi(); GrpcAPI.DecryptNotes srcNotes = queryNoteByIvk(startNum, endNum, ivk); GrpcAPI.DecryptNotesMarked.Builder builder = GrpcAPI.DecryptNotesMarked.newBuilder(); @@ -3403,7 +3385,7 @@ public GrpcAPI.DecryptNotesMarked scanAndMarkNoteByIvk(long startNum, long endNu */ public GrpcAPI.DecryptNotes scanNoteByOvk(long startNum, long endNum, byte[] ovk) throws BadItemException, ZksnarkException { - checkFullNodeAllowShieldedTransaction(); + checkAllowShieldedTransactionApi(); GrpcAPI.DecryptNotes.Builder builder = GrpcAPI.DecryptNotes.newBuilder(); if (!(startNum >= 0 && endNum > startNum && endNum - startNum <= 1000)) { @@ -3543,7 +3525,7 @@ private void buildShieldedTRC20Output(ShieldedTRC20ParametersBuilder builder, public ShieldedTRC20Parameters createShieldedContractParameters( PrivateShieldedTRC20Parameters request) throws ContractValidateException, ZksnarkException, ContractExeException { - checkFullNodeAllowShieldedTransaction(); + checkAllowShieldedTransactionApi(); ShieldedTRC20ParametersBuilder builder = new ShieldedTRC20ParametersBuilder(); @@ -3680,7 +3662,7 @@ private void buildShieldedTRC20InputWithAK( public ShieldedTRC20Parameters createShieldedContractParametersWithoutAsk( PrivateShieldedTRC20ParametersWithoutAsk request) throws ZksnarkException, ContractValidateException, ContractExeException { - checkFullNodeAllowShieldedTransaction(); + checkAllowShieldedTransactionApi(); ShieldedTRC20ParametersBuilder builder = new ShieldedTRC20ParametersBuilder(); byte[] shieldedTRC20ContractAddress = request.getShieldedTRC20ContractAddress().toByteArray(); @@ -3976,7 +3958,7 @@ public DecryptNotesTRC20 scanShieldedTRC20NotesByIvk( long startNum, long endNum, byte[] shieldedTRC20ContractAddress, byte[] ivk, byte[] ak, byte[] nk, ProtocolStringList topicsList) throws BadItemException, ZksnarkException, ContractExeException { - checkFullNodeAllowShieldedTransaction(); + checkAllowShieldedTransactionApi(); return queryTRC20NoteByIvk(startNum, endNum, shieldedTRC20ContractAddress, ivk, ak, nk, topicsList); @@ -4055,7 +4037,7 @@ private Optional getNoteTxFromLogListByOvk( public DecryptNotesTRC20 scanShieldedTRC20NotesByOvk(long startNum, long endNum, byte[] ovk, byte[] shieldedTRC20ContractAddress, ProtocolStringList topicsList) throws ZksnarkException, BadItemException { - checkFullNodeAllowShieldedTransaction(); + checkAllowShieldedTransactionApi(); DecryptNotesTRC20.Builder builder = DecryptNotesTRC20.newBuilder(); if (!(startNum >= 0 && endNum > startNum && endNum - startNum <= 1000)) { @@ -4118,7 +4100,7 @@ private byte[] getShieldedTRC20Nullifier(GrpcAPI.Note note, long pos, byte[] ak, public GrpcAPI.NullifierResult isShieldedTRC20ContractNoteSpent(NfTRC20Parameters request) throws ZksnarkException, ContractExeException { - checkFullNodeAllowShieldedTransaction(); + checkAllowShieldedTransactionApi(); return GrpcAPI.NullifierResult.newBuilder() .setIsSpent(isShieldedTRC20NoteSpent(request.getNote(), @@ -4241,7 +4223,7 @@ public byte[] getShieldedContractScalingFactor(byte[] contractAddress) public BytesMessage getTriggerInputForShieldedTRC20Contract( ShieldedTRC20TriggerContractParameters request) throws ZksnarkException, ContractValidateException { - checkFullNodeAllowShieldedTransaction(); + checkAllowShieldedTransactionApi(); ShieldedTRC20Parameters shieldedTRC20Parameters = request.getShieldedTRC20Parameters(); List spendAuthoritySignature = request.getSpendAuthoritySignatureList(); diff --git a/framework/src/main/java/org/tron/core/config/args/Args.java b/framework/src/main/java/org/tron/core/config/args/Args.java index 6f9d7ed4cec..3c162e03549 100644 --- a/framework/src/main/java/org/tron/core/config/args/Args.java +++ b/framework/src/main/java/org/tron/core/config/args/Args.java @@ -191,7 +191,7 @@ public static void clearParam() { PARAMETER.maxHttpConnectNumber = 50; PARAMETER.allowMultiSign = 0; PARAMETER.trxExpirationTimeInMilliseconds = 0; - PARAMETER.fullNodeAllowShieldedTransactionArgs = true; + PARAMETER.allowShieldedTransactionApi = true; PARAMETER.zenTokenId = "000000"; PARAMETER.allowProtoFilterNum = 0; PARAMETER.allowAccountStateRoot = 0; @@ -998,9 +998,18 @@ public static void setParam(final Config config) { PARAMETER.eventFilter = config.hasPath(Constant.EVENT_SUBSCRIBE_FILTER) ? getEventFilter(config) : null; - PARAMETER.fullNodeAllowShieldedTransactionArgs = - !config.hasPath(Constant.NODE_FULLNODE_ALLOW_SHIELDED_TRANSACTION) - || config.getBoolean(Constant.NODE_FULLNODE_ALLOW_SHIELDED_TRANSACTION); + if (config.hasPath(Constant.ALLOW_SHIELDED_TRANSACTION_API)) { + PARAMETER.allowShieldedTransactionApi = + config.getBoolean(Constant.ALLOW_SHIELDED_TRANSACTION_API); + } else if (config.hasPath(Constant.NODE_FULLNODE_ALLOW_SHIELDED_TRANSACTION)) { + // for compatibility with previous configuration + PARAMETER.allowShieldedTransactionApi = + config.getBoolean(Constant.NODE_FULLNODE_ALLOW_SHIELDED_TRANSACTION); + logger.warn("Configuring [node.fullNodeAllowShieldedTransaction] will be deprecated. " + + "Please use [node.allowShieldedTransactionApi] instead."); + } else { + PARAMETER.allowShieldedTransactionApi = true; + } PARAMETER.zenTokenId = config.hasPath(Constant.NODE_ZEN_TOKENID) ? config.getString(Constant.NODE_ZEN_TOKENID) : "000000"; @@ -1037,10 +1046,6 @@ public static void setParam(final Config config) { config.hasPath(Constant.NODE_SHIELDED_TRANS_IN_PENDING_MAX_COUNTS) ? config .getInt(Constant.NODE_SHIELDED_TRANS_IN_PENDING_MAX_COUNTS) : 10; - if (PARAMETER.isWitness()) { - PARAMETER.fullNodeAllowShieldedTransactionArgs = true; - } - PARAMETER.rateLimiterGlobalQps = config.hasPath(Constant.RATE_LIMITER_GLOBAL_QPS) ? config .getInt(Constant.RATE_LIMITER_GLOBAL_QPS) : 50000; @@ -1812,10 +1817,6 @@ public static void logConfig() { logger.info("\n"); } - public static void setFullNodeAllowShieldedTransaction(boolean fullNodeAllowShieldedTransaction) { - PARAMETER.fullNodeAllowShieldedTransactionArgs = fullNodeAllowShieldedTransaction; - } - private static void witnessAddressCheck(Config config) { if (config.hasPath(Constant.LOCAL_WITNESS_ACCOUNT_ADDRESS)) { byte[] bytes = Commons diff --git a/framework/src/main/java/org/tron/core/db/Manager.java b/framework/src/main/java/org/tron/core/db/Manager.java index 1eecc103874..81c4a941628 100644 --- a/framework/src/main/java/org/tron/core/db/Manager.java +++ b/framework/src/main/java/org/tron/core/db/Manager.java @@ -868,9 +868,9 @@ public boolean pushTransaction(final TransactionCapsule trx) TooBigTransactionException, TransactionExpirationException, ReceiptCheckErrException, VMIllegalException, TooBigTransactionResultException { - if (isShieldedTransaction(trx.getInstance()) && !Args.getInstance() - .isFullNodeAllowShieldedTransactionArgs()) { - return true; + if (isShieldedTransaction(trx.getInstance()) && !chainBaseManager.getDynamicPropertiesStore() + .supportShieldedTransaction()) { + return false; } pushTransactionQueue.add(trx); diff --git a/framework/src/main/java/org/tron/core/services/RpcApiService.java b/framework/src/main/java/org/tron/core/services/RpcApiService.java index 8f9c6b15bb7..b443d470b0a 100755 --- a/framework/src/main/java/org/tron/core/services/RpcApiService.java +++ b/framework/src/main/java/org/tron/core/services/RpcApiService.java @@ -291,20 +291,6 @@ private StatusRuntimeException getRunTimeException(Exception e) { } } - private void checkSupportShieldedTransaction() throws ZksnarkException { - String msg = "Not support Shielded Transaction, need to be opened by the committee"; - if (!dbManager.getDynamicPropertiesStore().supportShieldedTransaction()) { - throw new ZksnarkException(msg); - } - } - - private void checkSupportShieldedTRC20Transaction() throws ZksnarkException { - String msg = "Not support Shielded TRC20 Transaction, need to be opened by the committee"; - if (!dbManager.getDynamicPropertiesStore().supportShieldedTRC20Transaction()) { - throw new ZksnarkException(msg); - } - } - /** * DatabaseApi. */ @@ -651,8 +637,6 @@ public void getMerkleTreeVoucherInfo(OutputPointInfo request, StreamObserver responseObserver) { try { - checkSupportShieldedTransaction(); - IncrementalMerkleVoucherInfo witnessInfo = wallet .getMerkleTreeVoucherInfo(request); responseObserver.onNext(witnessInfo); @@ -669,8 +653,6 @@ public void scanNoteByIvk(GrpcAPI.IvkDecryptParameters request, long endNum = request.getEndBlockIndex(); try { - checkSupportShieldedTransaction(); - DecryptNotes decryptNotes = wallet .scanNoteByIvk(startNum, endNum, request.getIvk().toByteArray()); responseObserver.onNext(decryptNotes); @@ -687,8 +669,6 @@ public void scanAndMarkNoteByIvk(GrpcAPI.IvkDecryptAndMarkParameters request, long endNum = request.getEndBlockIndex(); try { - checkSupportShieldedTransaction(); - DecryptNotesMarked decryptNotes = wallet.scanAndMarkNoteByIvk(startNum, endNum, request.getIvk().toByteArray(), request.getAk().toByteArray(), @@ -707,8 +687,6 @@ public void scanNoteByOvk(GrpcAPI.OvkDecryptParameters request, long startNum = request.getStartBlockIndex(); long endNum = request.getEndBlockIndex(); try { - checkSupportShieldedTransaction(); - DecryptNotes decryptNotes = wallet .scanNoteByOvk(startNum, endNum, request.getOvk().toByteArray()); responseObserver.onNext(decryptNotes); @@ -721,8 +699,6 @@ public void scanNoteByOvk(GrpcAPI.OvkDecryptParameters request, @Override public void isSpend(NoteParameters request, StreamObserver responseObserver) { try { - checkSupportShieldedTransaction(); - responseObserver.onNext(wallet.isSpend(request)); } catch (Exception e) { responseObserver.onError(getRunTimeException(e)); @@ -742,7 +718,6 @@ public void scanShieldedTRC20NotesByIvk(IvkDecryptTRC20Parameters request, ProtocolStringList topicsList = request.getEventsList(); try { - checkSupportShieldedTRC20Transaction(); responseObserver.onNext( wallet.scanShieldedTRC20NotesByIvk(startNum, endNum, contractAddress, ivk, ak, nk, topicsList)); @@ -762,7 +737,6 @@ public void scanShieldedTRC20NotesByOvk(OvkDecryptTRC20Parameters request, byte[] ovk = request.getOvk().toByteArray(); ProtocolStringList topicList = request.getEventsList(); try { - checkSupportShieldedTRC20Transaction(); responseObserver .onNext(wallet .scanShieldedTRC20NotesByOvk(startNum, endNum, ovk, contractAddress, topicList)); @@ -776,7 +750,6 @@ public void scanShieldedTRC20NotesByOvk(OvkDecryptTRC20Parameters request, public void isShieldedTRC20ContractNoteSpent(NfTRC20Parameters request, StreamObserver responseObserver) { try { - checkSupportShieldedTRC20Transaction(); responseObserver.onNext(wallet.isShieldedTRC20ContractNoteSpent(request)); } catch (Exception e) { responseObserver.onError(getRunTimeException(e)); @@ -2066,8 +2039,6 @@ public void getMerkleTreeVoucherInfo(OutputPointInfo request, StreamObserver responseObserver) { try { - checkSupportShieldedTransaction(); - IncrementalMerkleVoucherInfo witnessInfo = wallet .getMerkleTreeVoucherInfo(request); responseObserver.onNext(witnessInfo); @@ -2087,8 +2058,6 @@ public void createShieldedTransaction(PrivateParameters request, Return.Builder retBuilder = Return.newBuilder(); try { - checkSupportShieldedTransaction(); - TransactionCapsule trx = wallet.createShieldedTransaction(request); trxExtBuilder.setTransaction(trx.getInstance()); trxExtBuilder.setTxid(trx.getTransactionId().getByteString()); @@ -2118,8 +2087,6 @@ public void createShieldedTransactionWithoutSpendAuthSig(PrivateParametersWithou Return.Builder retBuilder = Return.newBuilder(); try { - checkSupportShieldedTransaction(); - TransactionCapsule trx = wallet.createShieldedTransactionWithoutSpendAuthSig(request); trxExtBuilder.setTransaction(trx.getInstance()); trxExtBuilder.setTxid(trx.getTransactionId().getByteString()); @@ -2147,8 +2114,6 @@ public void getNewShieldedAddress(EmptyMessage request, StreamObserver responseObserver) { try { - checkSupportShieldedTRC20Transaction(); - responseObserver.onNext(wallet.getNewShieldedAddress()); } catch (Exception e) { responseObserver.onError(getRunTimeException(e)); @@ -2161,8 +2126,6 @@ public void getNewShieldedAddress(EmptyMessage request, public void getSpendingKey(EmptyMessage request, StreamObserver responseObserver) { try { - checkSupportShieldedTRC20Transaction(); - responseObserver.onNext(wallet.getSpendingKey()); } catch (Exception e) { responseObserver.onError(getRunTimeException(e)); @@ -2175,8 +2138,6 @@ public void getSpendingKey(EmptyMessage request, public void getRcm(EmptyMessage request, StreamObserver responseObserver) { try { - checkSupportShieldedTRC20Transaction(); - responseObserver.onNext(wallet.getRcm()); } catch (Exception e) { responseObserver.onError(getRunTimeException(e)); @@ -2191,8 +2152,6 @@ public void getExpandedSpendingKey(BytesMessage request, ByteString spendingKey = request.getValue(); try { - checkSupportShieldedTRC20Transaction(); - ExpandedSpendingKeyMessage response = wallet.getExpandedSpendingKey(spendingKey); responseObserver.onNext(response); } catch (BadItemException | ZksnarkException e) { @@ -2208,8 +2167,6 @@ public void getAkFromAsk(BytesMessage request, StreamObserver resp ByteString ak = request.getValue(); try { - checkSupportShieldedTRC20Transaction(); - responseObserver.onNext(wallet.getAkFromAsk(ak)); } catch (BadItemException | ZksnarkException e) { responseObserver.onError(getRunTimeException(e)); @@ -2224,8 +2181,6 @@ public void getNkFromNsk(BytesMessage request, StreamObserver resp ByteString nk = request.getValue(); try { - checkSupportShieldedTRC20Transaction(); - responseObserver.onNext(wallet.getNkFromNsk(nk)); } catch (BadItemException | ZksnarkException e) { responseObserver.onError(getRunTimeException(e)); @@ -2242,8 +2197,6 @@ public void getIncomingViewingKey(ViewingKeyMessage request, ByteString nk = request.getNk(); try { - checkSupportShieldedTRC20Transaction(); - responseObserver.onNext(wallet.getIncomingViewingKey(ak.toByteArray(), nk.toByteArray())); } catch (ZksnarkException e) { responseObserver.onError(getRunTimeException(e)); @@ -2257,8 +2210,6 @@ public void getIncomingViewingKey(ViewingKeyMessage request, public void getDiversifier(EmptyMessage request, StreamObserver responseObserver) { try { - checkSupportShieldedTRC20Transaction(); - DiversifierMessage d = wallet.getDiversifier(); responseObserver.onNext(d); } catch (ZksnarkException e) { @@ -2276,8 +2227,6 @@ public void getZenPaymentAddress(IncomingViewingKeyDiversifierMessage request, DiversifierMessage d = request.getD(); try { - checkSupportShieldedTRC20Transaction(); - PaymentAddressMessage saplingPaymentAddressMessage = wallet.getPaymentAddress(new IncomingViewingKey(ivk.getIvk().toByteArray()), new DiversifierT(d.getD().toByteArray())); @@ -2298,8 +2247,6 @@ public void scanNoteByIvk(GrpcAPI.IvkDecryptParameters request, long endNum = request.getEndBlockIndex(); try { - checkSupportShieldedTransaction(); - DecryptNotes decryptNotes = wallet .scanNoteByIvk(startNum, endNum, request.getIvk().toByteArray()); responseObserver.onNext(decryptNotes); @@ -2318,8 +2265,6 @@ public void scanAndMarkNoteByIvk(GrpcAPI.IvkDecryptAndMarkParameters request, long endNum = request.getEndBlockIndex(); try { - checkSupportShieldedTransaction(); - DecryptNotesMarked decryptNotes = wallet.scanAndMarkNoteByIvk(startNum, endNum, request.getIvk().toByteArray(), request.getAk().toByteArray(), @@ -2340,8 +2285,6 @@ public void scanNoteByOvk(GrpcAPI.OvkDecryptParameters request, long endNum = request.getEndBlockIndex(); try { - checkSupportShieldedTransaction(); - DecryptNotes decryptNotes = wallet .scanNoteByOvk(startNum, endNum, request.getOvk().toByteArray()); responseObserver.onNext(decryptNotes); @@ -2355,8 +2298,6 @@ public void scanNoteByOvk(GrpcAPI.OvkDecryptParameters request, @Override public void isSpend(NoteParameters request, StreamObserver responseObserver) { try { - checkSupportShieldedTransaction(); - responseObserver.onNext(wallet.isSpend(request)); } catch (Exception e) { responseObserver.onError(getRunTimeException(e)); @@ -2369,8 +2310,6 @@ public void isSpend(NoteParameters request, StreamObserver response public void createShieldNullifier(GrpcAPI.NfParameters request, StreamObserver responseObserver) { try { - checkSupportShieldedTransaction(); - BytesMessage nf = wallet .createShieldNullifier(request); responseObserver.onNext(nf); @@ -2385,8 +2324,6 @@ public void createShieldNullifier(GrpcAPI.NfParameters request, public void createSpendAuthSig(SpendAuthSigParameters request, StreamObserver responseObserver) { try { - checkSupportShieldedTRC20Transaction(); - BytesMessage spendAuthSig = wallet.createSpendAuthSig(request); responseObserver.onNext(spendAuthSig); } catch (Exception e) { @@ -2400,8 +2337,6 @@ public void createSpendAuthSig(SpendAuthSigParameters request, public void getShieldTransactionHash(Transaction request, StreamObserver responseObserver) { try { - checkSupportShieldedTransaction(); - BytesMessage transactionHash = wallet.getShieldTransactionHash(request); responseObserver.onNext(transactionHash); } catch (Exception e) { @@ -2416,8 +2351,6 @@ public void createShieldedContractParameters( PrivateShieldedTRC20Parameters request, StreamObserver responseObserver) { try { - checkSupportShieldedTRC20Transaction(); - ShieldedTRC20Parameters shieldedTRC20Parameters = wallet .createShieldedContractParameters(request); responseObserver.onNext(shieldedTRC20Parameters); @@ -2438,8 +2371,6 @@ public void createShieldedContractParametersWithoutAsk( PrivateShieldedTRC20ParametersWithoutAsk request, StreamObserver responseObserver) { try { - checkSupportShieldedTRC20Transaction(); - ShieldedTRC20Parameters shieldedTRC20Parameters = wallet .createShieldedContractParametersWithoutAsk(request); responseObserver.onNext(shieldedTRC20Parameters); @@ -2459,8 +2390,6 @@ public void scanShieldedTRC20NotesByIvk( long startNum = request.getStartBlockIndex(); long endNum = request.getEndBlockIndex(); try { - checkSupportShieldedTRC20Transaction(); - DecryptNotesTRC20 decryptNotes = wallet.scanShieldedTRC20NotesByIvk(startNum, endNum, request.getShieldedTRC20ContractAddress().toByteArray(), request.getIvk().toByteArray(), @@ -2487,8 +2416,6 @@ public void scanShieldedTRC20NotesByOvk( long startNum = request.getStartBlockIndex(); long endNum = request.getEndBlockIndex(); try { - checkSupportShieldedTRC20Transaction(); - DecryptNotesTRC20 decryptNotes = wallet.scanShieldedTRC20NotesByOvk(startNum, endNum, request.getOvk().toByteArray(), request.getShieldedTRC20ContractAddress().toByteArray(), @@ -2506,8 +2433,6 @@ public void scanShieldedTRC20NotesByOvk( public void isShieldedTRC20ContractNoteSpent(NfTRC20Parameters request, StreamObserver responseObserver) { try { - checkSupportShieldedTRC20Transaction(); - GrpcAPI.NullifierResult nf = wallet .isShieldedTRC20ContractNoteSpent(request); responseObserver.onNext(nf); @@ -2523,8 +2448,6 @@ public void getTriggerInputForShieldedTRC20Contract( ShieldedTRC20TriggerContractParameters request, StreamObserver responseObserver) { try { - checkSupportShieldedTRC20Transaction(); - responseObserver.onNext(wallet.getTriggerInputForShieldedTRC20Contract(request)); } catch (Exception e) { responseObserver.onError(e); diff --git a/framework/src/main/java/org/tron/core/zen/ZksnarkInitService.java b/framework/src/main/java/org/tron/core/zen/ZksnarkInitService.java index a1f812426f7..dfc4b428836 100644 --- a/framework/src/main/java/org/tron/core/zen/ZksnarkInitService.java +++ b/framework/src/main/java/org/tron/core/zen/ZksnarkInitService.java @@ -3,6 +3,7 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.util.concurrent.atomic.AtomicBoolean; import javax.annotation.PostConstruct; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.FileUtils; @@ -16,34 +17,43 @@ @Component public class ZksnarkInitService { + private static final AtomicBoolean initialized = new AtomicBoolean(false); + @PostConstruct private void init() { librustzcashInitZksnarkParams(); } public static void librustzcashInitZksnarkParams() { - logger.info("init zk param begin"); - - if (!JLibrustzcash.isOpenZen()) { - logger.info("zen switch is off, zen will not start."); + if (initialized.get()) { + logger.info("zk param already initialized"); return; } - String spendPath = getParamsFile("sapling-spend.params"); - String spendHash = "25fd9a0d1c1be0526c14662947ae95b758fe9f3d7fb7f55e9b4437830dcc6215a7ce3ea465" - + "914b157715b7a4d681389ea4aa84438190e185d5e4c93574d3a19a"; + synchronized (ZksnarkInitService.class) { + if (initialized.get()) { + logger.info("zk param already initialized"); + return; + } + logger.info("init zk param begin"); - String outputPath = getParamsFile("sapling-output.params"); - String outputHash = "a1cb23b93256adce5bce2cb09cefbc96a1d16572675ceb691e9a3626ec15b5b546926ff1c" - + "536cfe3a9df07d796b32fdfc3e5d99d65567257bf286cd2858d71a6"; + String spendPath = getParamsFile("sapling-spend.params"); + String spendHash = "25fd9a0d1c1be0526c14662947ae95b758fe9f3d7fb7f55e9b4437830dcc6215a7ce3ea" + + "465914b157715b7a4d681389ea4aa84438190e185d5e4c93574d3a19a"; - try { - JLibrustzcash.librustzcashInitZksnarkParams( - new LibrustzcashParam.InitZksnarkParams(spendPath, spendHash, outputPath, outputHash)); - } catch (ZksnarkException e) { - throw new TronError(e, TronError.ErrCode.ZCASH_INIT); + String outputPath = getParamsFile("sapling-output.params"); + String outputHash = "a1cb23b93256adce5bce2cb09cefbc96a1d16572675ceb691e9a3626ec15b5b546926f" + + "f1c536cfe3a9df07d796b32fdfc3e5d99d65567257bf286cd2858d71a6"; + + try { + JLibrustzcash.librustzcashInitZksnarkParams( + new LibrustzcashParam.InitZksnarkParams(spendPath, spendHash, outputPath, outputHash)); + } catch (ZksnarkException e) { + throw new TronError(e, TronError.ErrCode.ZCASH_INIT); + } + initialized.set(true); + logger.info("init zk param done"); } - logger.info("init zk param done"); } private static String getParamsFile(String fileName) { @@ -61,4 +71,4 @@ private static String getParamsFile(String fileName) { } return fileOut.getAbsolutePath(); } -} \ No newline at end of file +} diff --git a/framework/src/test/java/org/tron/core/ShieldWalletTest.java b/framework/src/test/java/org/tron/core/ShieldWalletTest.java index f8d5db1a44c..6e35d600ce7 100644 --- a/framework/src/test/java/org/tron/core/ShieldWalletTest.java +++ b/framework/src/test/java/org/tron/core/ShieldWalletTest.java @@ -7,6 +7,7 @@ import java.math.BigInteger; import javax.annotation.Resource; import org.junit.Assert; +import org.junit.BeforeClass; import org.junit.Test; import org.tron.api.GrpcAPI.PrivateParameters; import org.tron.api.GrpcAPI.PrivateParametersWithoutAsk; @@ -29,13 +30,14 @@ public class ShieldWalletTest extends BaseTest { @Resource private Wallet wallet; - static { - Args.setParam(new String[] {"-d", dbPath()}, Constant.TEST_CONF); + @BeforeClass + public static void init() { + Args.setParam(new String[]{"-d", dbPath()}, Constant.TEST_CONF); + librustzcashInitZksnarkParams(); } @Test public void testCreateShieldedTransaction1() { - librustzcashInitZksnarkParams(); String transactionStr1 = new String(ByteArray.fromHexString( "0x7b0a20202020227472616e73706172656e745f66726f6d5f61646472657373223a202234433930413" + "73241433344414546324536383932343545463430303839443634314345414337373433323433414233" @@ -68,7 +70,6 @@ public void testCreateShieldedTransaction1() { @Test public void testCreateShieldedTransaction2() { - librustzcashInitZksnarkParams(); String transactionStr2 = new String(ByteArray.fromHexString( "7b0a202020202261736b223a20223938666430333136376632333437623534643737323338343137663" + "6373038643537323939643938376362613838353564653037626532346236316464653064222c0a2020" @@ -176,7 +177,6 @@ public void testCreateShieldedTransaction2() { @Test public void testCreateShieldedTransactionWithoutSpendAuthSig() { - librustzcashInitZksnarkParams(); String transactionStr3 = new String(ByteArray.fromHexString( "7b0a2020202022616b223a2022373161643638633466353035373464356164333735343863626538363" + "63031663732393662393161306362303535353733313462373830383437323730326465222c0a202020" @@ -286,7 +286,6 @@ public void testCreateShieldedTransactionWithoutSpendAuthSig() { @Test public void testGetNewShieldedAddress() { - librustzcashInitZksnarkParams(); try { ShieldedAddressInfo shieldedAddressInfo = wallet.getNewShieldedAddress(); Assert.assertNotNull(shieldedAddressInfo); @@ -297,8 +296,7 @@ public void testGetNewShieldedAddress() { @Test public void testCreateShieldedContractParameters() throws ContractExeException { - librustzcashInitZksnarkParams(); - Args.getInstance().setFullNodeAllowShieldedTransactionArgs(true); + Args.getInstance().setAllowShieldedTransactionApi(true); Wallet wallet1 = spy(new Wallet()); doReturn(BigInteger.valueOf(1).toByteArray()) @@ -340,8 +338,7 @@ public void testCreateShieldedContractParameters() throws ContractExeException { @Test public void testCreateShieldedContractParameters2() throws ContractExeException { - librustzcashInitZksnarkParams(); - Args.getInstance().setFullNodeAllowShieldedTransactionArgs(true); + Args.getInstance().setAllowShieldedTransactionApi(true); Wallet wallet1 = spy(new Wallet()); doReturn(BigInteger.valueOf(1).toByteArray()) @@ -416,8 +413,7 @@ public void testCreateShieldedContractParameters2() throws ContractExeException @Test public void testCreateShieldedContractParametersWithoutAsk() throws ContractExeException { - librustzcashInitZksnarkParams(); - Args.getInstance().setFullNodeAllowShieldedTransactionArgs(true); + Args.getInstance().setAllowShieldedTransactionApi(true); Wallet wallet1 = spy(new Wallet()); doReturn(BigInteger.valueOf(1).toByteArray()) diff --git a/framework/src/test/java/org/tron/core/ShieldedTRC20BuilderTest.java b/framework/src/test/java/org/tron/core/ShieldedTRC20BuilderTest.java index 2c97473b6c3..c2c4bfe3006 100644 --- a/framework/src/test/java/org/tron/core/ShieldedTRC20BuilderTest.java +++ b/framework/src/test/java/org/tron/core/ShieldedTRC20BuilderTest.java @@ -1,7 +1,5 @@ package org.tron.core; -import static org.tron.core.zksnark.LibrustzcashTest.librustzcashInitZksnarkParams; - import com.google.protobuf.ByteString; import java.math.BigInteger; import java.util.Arrays; @@ -12,7 +10,7 @@ import org.apache.commons.lang3.tuple.Pair; import org.bouncycastle.util.encoders.Hex; import org.junit.Assert; -import org.junit.Before; +import org.junit.BeforeClass; import org.junit.Ignore; import org.junit.Test; import org.tron.api.GrpcAPI; @@ -68,7 +66,6 @@ public class ShieldedTRC20BuilderTest extends BaseTest { SHIELDED_CONTRACT_ADDRESS = WalletClient.decodeFromBase58Check(SHIELDED_CONTRACT_ADDRESS_STR); DEFAULT_OVK = ByteArray .fromHexString("030c8c2bc59fb3eb8afb047a8ea4b028743d23e7d38c6fa30908358431e2314d"); - ZksnarkInitService.librustzcashInitZksnarkParams(); PUBLIC_TO_ADDRESS = WalletClient.decodeFromBase58Check(PUBLIC_TO_ADDRESS_STR); } @@ -76,8 +73,9 @@ public class ShieldedTRC20BuilderTest extends BaseTest { VerifyTransferProof transferContract = new VerifyTransferProof(); VerifyBurnProof burnContract = new VerifyBurnProof(); - @Before - public void before() { + @BeforeClass + public static void initZksnarkParams() { + ZksnarkInitService.librustzcashInitZksnarkParams(); } @Ignore @@ -2172,7 +2170,6 @@ public void createShieldedContractParametersWithoutAskForBurn1to2() @Ignore @Test public void getTriggerInputForForMint() throws Exception { - librustzcashInitZksnarkParams(); SpendingKey sk = SpendingKey.random(); ExpandedSpendingKey expsk = sk.expandedSpendingKey(); byte[] ovk = expsk.getOvk(); @@ -2241,7 +2238,6 @@ public void getTriggerInputForForMint() throws Exception { public void testScanShieldedTRC20NotesByIvk() throws Exception { int statNum = 1; int endNum = 100; - librustzcashInitZksnarkParams(); SpendingKey sk = SpendingKey.decode(priKey); FullViewingKey fvk = sk.fullViewingKey(); byte[] ivk = fvk.inViewingKey().value; @@ -2273,7 +2269,6 @@ public void testscanShieldedTRC20NotesByOvk() throws Exception { public void isShieldedTRC20ContractNoteSpent() throws Exception { int statNum = 9200; int endNum = 9240; - librustzcashInitZksnarkParams(); SpendingKey sk = SpendingKey.decode(priKey); FullViewingKey fvk = sk.fullViewingKey(); byte[] ivk = fvk.inViewingKey().value; @@ -2350,7 +2345,6 @@ private byte[] decodePath(byte[] encodedPath) { private GrpcAPI.PrivateShieldedTRC20Parameters mintParams(String privKey, long value, String contractAddr, byte[] rcm) throws ZksnarkException, ContractValidateException { - librustzcashInitZksnarkParams(); long fromAmount = value; SpendingKey sk = SpendingKey.decode(privKey); ExpandedSpendingKey expsk = sk.expandedSpendingKey(); diff --git a/framework/src/test/java/org/tron/core/WalletMockTest.java b/framework/src/test/java/org/tron/core/WalletMockTest.java index 098ba9aee61..ab7ad7ba10c 100644 --- a/framework/src/test/java/org/tron/core/WalletMockTest.java +++ b/framework/src/test/java/org/tron/core/WalletMockTest.java @@ -835,7 +835,7 @@ public void testGetTriggerInputForShieldedTRC20Contract() { CommonParameter commonParameterMock = mock(Args.class); try (MockedStatic mockedStatic = mockStatic(CommonParameter.class)) { when(CommonParameter.getInstance()).thenReturn(commonParameterMock); - when(commonParameterMock.isFullNodeAllowShieldedTransactionArgs()).thenReturn(true); + when(commonParameterMock.isAllowShieldedTransactionApi()).thenReturn(true); assertThrows(ZksnarkException.class, () -> { wallet.getTriggerInputForShieldedTRC20Contract(triggerParam.build()); @@ -866,7 +866,7 @@ public void testGetTriggerInputForShieldedTRC20Contract1() CommonParameter commonParameterMock = mock(Args.class); try (MockedStatic mockedStatic = mockStatic(CommonParameter.class)) { when(CommonParameter.getInstance()).thenReturn(commonParameterMock); - when(commonParameterMock.isFullNodeAllowShieldedTransactionArgs()).thenReturn(true); + when(commonParameterMock.isAllowShieldedTransactionApi()).thenReturn(true); GrpcAPI.BytesMessage reponse = wallet.getTriggerInputForShieldedTRC20Contract(triggerParam.build()); @@ -1319,4 +1319,4 @@ public void testGetContractInfo1() throws Exception { wallet.getContractInfo(bytesMessage); assertNotNull(smartContractDataWrapper); } -} \ No newline at end of file +} diff --git a/framework/src/test/java/org/tron/core/actuator/ShieldedTransferActuatorTest.java b/framework/src/test/java/org/tron/core/actuator/ShieldedTransferActuatorTest.java index b71ba432018..cb95194f3d3 100755 --- a/framework/src/test/java/org/tron/core/actuator/ShieldedTransferActuatorTest.java +++ b/framework/src/test/java/org/tron/core/actuator/ShieldedTransferActuatorTest.java @@ -85,7 +85,7 @@ public class ShieldedTransferActuatorTest extends BaseTest { */ @BeforeClass public static void init() throws ZksnarkException { - Args.setFullNodeAllowShieldedTransaction(true); + Args.getInstance().setAllowShieldedTransactionApi(true); librustzcashInitZksnarkParams(); } @@ -950,7 +950,7 @@ public void publicAddressAToShieldAddressNoToAddressFailure() { */ @Test public void publicToShieldAddressAndShieldToPublicAddressWithZoreValueSuccess() { - Args.setFullNodeAllowShieldedTransaction(true); + Args.getInstance().setAllowShieldedTransactionApi(true); dbManager.getDynamicPropertiesStore().saveAllowShieldedTransaction(1); long fee = dbManager.getDynamicPropertiesStore().getShieldedTransactionFee(); diff --git a/framework/src/test/java/org/tron/core/exception/TronErrorTest.java b/framework/src/test/java/org/tron/core/exception/TronErrorTest.java index b4c3dc4b07f..98d5596e251 100644 --- a/framework/src/test/java/org/tron/core/exception/TronErrorTest.java +++ b/framework/src/test/java/org/tron/core/exception/TronErrorTest.java @@ -59,7 +59,6 @@ public void testTronError() { @Test public void ZksnarkInitTest() { try (MockedStatic mock = mockStatic(JLibrustzcash.class)) { - mock.when(JLibrustzcash::isOpenZen).thenReturn(true); mock.when(() -> JLibrustzcash.librustzcashInitZksnarkParams(any())) .thenAnswer(invocation -> { throw new ZksnarkException("Zksnark init failed"); diff --git a/framework/src/test/java/org/tron/core/services/filter/HttpApiAccessFilterTest.java b/framework/src/test/java/org/tron/core/services/filter/HttpApiAccessFilterTest.java index 420d890aa48..0d2c9c9ae78 100644 --- a/framework/src/test/java/org/tron/core/services/filter/HttpApiAccessFilterTest.java +++ b/framework/src/test/java/org/tron/core/services/filter/HttpApiAccessFilterTest.java @@ -38,7 +38,7 @@ public class HttpApiAccessFilterTest extends BaseTest { static { Args.setParam(new String[]{"-d", dbPath()}, Constant.TEST_CONF); - Args.getInstance().setFullNodeAllowShieldedTransactionArgs(false); + Args.getInstance().setAllowShieldedTransactionApi(false); Args.getInstance().setFullNodeHttpEnable(true); Args.getInstance().setFullNodeHttpPort(PublicMethod.chooseRandomPort()); Args.getInstance().setPBFTHttpEnable(true); diff --git a/framework/src/test/java/org/tron/core/services/filter/LiteFnQueryHttpFilterTest.java b/framework/src/test/java/org/tron/core/services/filter/LiteFnQueryHttpFilterTest.java index 0f0bdf1eb1f..978042a8578 100644 --- a/framework/src/test/java/org/tron/core/services/filter/LiteFnQueryHttpFilterTest.java +++ b/framework/src/test/java/org/tron/core/services/filter/LiteFnQueryHttpFilterTest.java @@ -31,7 +31,7 @@ public class LiteFnQueryHttpFilterTest extends BaseTest { static { Args.setParam(new String[]{"-d", dbPath()}, Constant.TEST_CONF); - Args.getInstance().setFullNodeAllowShieldedTransactionArgs(false); + Args.getInstance().setAllowShieldedTransactionApi(false); Args.getInstance().setRpcEnable(false); Args.getInstance().setRpcSolidityEnable(false); Args.getInstance().setRpcPBFTEnable(false); diff --git a/framework/src/test/java/org/tron/core/zksnark/LibrustzcashTest.java b/framework/src/test/java/org/tron/core/zksnark/LibrustzcashTest.java index 049fb2528b1..f3852952cc7 100644 --- a/framework/src/test/java/org/tron/core/zksnark/LibrustzcashTest.java +++ b/framework/src/test/java/org/tron/core/zksnark/LibrustzcashTest.java @@ -84,7 +84,8 @@ public static void init() { }, "config-test-mainnet.conf" ); - Args.setFullNodeAllowShieldedTransaction(true); + Args.getInstance().setAllowShieldedTransactionApi(true); + ZksnarkInitService.librustzcashInitZksnarkParams(); } private static int randomInt(int minInt, int maxInt) { @@ -115,10 +116,6 @@ public static void test(byte[] K, byte[] ovk, byte[] cv, byte[] cm, byte[] epk) null, 0, cipher_nonce, K))); } - public static void librustzcashInitZksnarkParams() { - ZksnarkInitService.librustzcashInitZksnarkParams(); - } - @Test public void testLibsodium() throws ZksnarkException { byte[] K = new byte[32]; @@ -275,7 +272,6 @@ public long benchmarkCreateSpend() throws ZksnarkException { @Ignore @Test public void calBenchmarkSpendConcurrent() throws Exception { - librustzcashInitZksnarkParams(); System.out.println("--- load ok ---"); int count = 2; @@ -307,7 +303,6 @@ public void calBenchmarkSpendConcurrent() throws Exception { @Test public void calBenchmarkSpend() throws ZksnarkException { - librustzcashInitZksnarkParams(); System.out.println("--- load ok ---"); int count = 2; @@ -374,7 +369,6 @@ public long benchmarkCreateSaplingSpend() throws BadItemException, ZksnarkExcept @Test public void calBenchmarkCreateSaplingSpend() throws BadItemException, ZksnarkException { - librustzcashInitZksnarkParams(); System.out.println("--- load ok ---"); int count = 2; @@ -451,7 +445,6 @@ public long benchmarkCreateSaplingOutput() throws BadItemException, ZksnarkExcep @Test public void calBenchmarkCreateSaplingOutPut() throws BadItemException, ZksnarkException { - librustzcashInitZksnarkParams(); System.out.println("--- load ok ---"); int count = 2; @@ -479,7 +472,6 @@ public void calBenchmarkCreateSaplingOutPut() throws BadItemException, ZksnarkEx @Test public void checkVerifyOutErr() throws ZksnarkException { - librustzcashInitZksnarkParams(); System.out.println("--- load ok ---"); // expect fail diff --git a/framework/src/test/java/org/tron/core/zksnark/SaplingNoteTest.java b/framework/src/test/java/org/tron/core/zksnark/SaplingNoteTest.java index da4df70d9ac..34913c98ccc 100644 --- a/framework/src/test/java/org/tron/core/zksnark/SaplingNoteTest.java +++ b/framework/src/test/java/org/tron/core/zksnark/SaplingNoteTest.java @@ -22,7 +22,7 @@ public class SaplingNoteTest { @BeforeClass public static void init() { - Args.setFullNodeAllowShieldedTransaction(true); + Args.getInstance().setAllowShieldedTransactionApi(true); // Args.getInstance().setAllowShieldedTransaction(1); ZksnarkInitService.librustzcashInitZksnarkParams(); } diff --git a/framework/src/test/java/org/tron/core/zksnark/SendCoinShieldTest.java b/framework/src/test/java/org/tron/core/zksnark/SendCoinShieldTest.java index 7746066abfa..e7dfa06d094 100644 --- a/framework/src/test/java/org/tron/core/zksnark/SendCoinShieldTest.java +++ b/framework/src/test/java/org/tron/core/zksnark/SendCoinShieldTest.java @@ -17,6 +17,7 @@ import lombok.extern.slf4j.Slf4j; import org.junit.Assert; import org.junit.Before; +import org.junit.BeforeClass; import org.junit.Ignore; import org.junit.Test; import org.tron.api.GrpcAPI; @@ -118,6 +119,11 @@ public class SendCoinShieldTest extends BaseTest { .fromHexString("030c8c2bc59fb3eb8afb047a8ea4b028743d23e7d38c6fa30908358431e2314d"); } + @BeforeClass + public static void initZksnarkParams() { + ZksnarkInitService.librustzcashInitZksnarkParams(); + } + /** * Init data. */ @@ -219,10 +225,6 @@ private IncrementalMerkleVoucherContainer createSimpleMerkleVoucherContainer(byt return tree.toVoucher(); } - private void librustzcashInitZksnarkParams() { - ZksnarkInitService.librustzcashInitZksnarkParams(); - } - @Test public void testStringRevert() { byte[] bytes = ByteArray @@ -235,7 +237,6 @@ public void testStringRevert() { @Test public void testGenerateSpendProof() throws Exception { - librustzcashInitZksnarkParams(); ZenTransactionBuilder builder = new ZenTransactionBuilder(wallet); SpendingKey sk = SpendingKey .decode("ff2c06269315333a9207f817d2eca0ac555ca8f90196976324c7756504e7c9ee"); @@ -270,7 +271,6 @@ public void testGenerateSpendProof() throws Exception { @Test public void generateOutputProof() throws ZksnarkException { - librustzcashInitZksnarkParams(); ZenTransactionBuilder builder = new ZenTransactionBuilder(wallet); SpendingKey spendingKey = SpendingKey.random(); FullViewingKey fullViewingKey = spendingKey.fullViewingKey(); @@ -289,7 +289,6 @@ public void generateOutputProof() throws ZksnarkException { @Test public void verifyOutputProof() throws ZksnarkException { - librustzcashInitZksnarkParams(); ZenTransactionBuilder builder = new ZenTransactionBuilder(wallet); SpendingKey spendingKey = SpendingKey.random(); FullViewingKey fullViewingKey = spendingKey.fullViewingKey(); @@ -321,7 +320,6 @@ public void verifyOutputProof() throws ZksnarkException { @Test public void testDecryptReceiveWithIvk() throws ZksnarkException { //verify c_enc - librustzcashInitZksnarkParams(); ZenTransactionBuilder builder = new ZenTransactionBuilder(); SpendingKey spendingKey = SpendingKey.random(); @@ -385,9 +383,6 @@ public String byte2intstring(byte[] input) { @Test public void testDecryptReceiveWithOvk() throws Exception { - //decode c_out with ovk. - librustzcashInitZksnarkParams(); - // construct payment address SpendingKey spendingKey2 = SpendingKey .decode("ff2c06269315333a9207f817d2eca0ac555ca8f90196976324c7756504e7c9ee"); @@ -467,7 +462,6 @@ public void pushShieldedTransactionAndDecryptWithIvk() AccountResourceInsufficientException, InvalidProtocolBufferException, ZksnarkException { long ctx = JLibrustzcash.librustzcashSaplingProvingCtxInit(); - librustzcashInitZksnarkParams(); dbManager.getDynamicPropertiesStore().saveAllowShieldedTransaction(1); dbManager.getDynamicPropertiesStore().saveTotalShieldedPoolValue(1000 * 1000000L); ZenTransactionBuilder builder = new ZenTransactionBuilder(wallet); @@ -556,7 +550,6 @@ public void pushShieldedTransactionAndDecryptWithOvk() AccountResourceInsufficientException, InvalidProtocolBufferException, ZksnarkException { long ctx = JLibrustzcash.librustzcashSaplingProvingCtxInit(); - librustzcashInitZksnarkParams(); dbManager.getDynamicPropertiesStore().saveAllowShieldedTransaction(1); dbManager.getDynamicPropertiesStore().saveTotalShieldedPoolValue(1000 * 1000000L); ZenTransactionBuilder builder = new ZenTransactionBuilder(wallet); @@ -643,10 +636,8 @@ private byte[] getHash() { @Ignore @Test public void checkZksnark() throws BadItemException, ZksnarkException { - librustzcashInitZksnarkParams(); long ctx = JLibrustzcash.librustzcashSaplingProvingCtxInit(); // generate spend proof - librustzcashInitZksnarkParams(); dbManager.getDynamicPropertiesStore().saveAllowShieldedTransaction(1); dbManager.getDynamicPropertiesStore().saveTotalShieldedPoolValue(4010 * 1000000L); ZenTransactionBuilder builder = new ZenTransactionBuilder(wallet); @@ -679,7 +670,6 @@ public void checkZksnark() throws BadItemException, ZksnarkException { @Test public void testVerifySpendProof() throws BadItemException, ZksnarkException { - librustzcashInitZksnarkParams(); ZenTransactionBuilder builder = new ZenTransactionBuilder(wallet); SpendingKey sk = SpendingKey .decode("ff2c06269315333a9207f817d2eca0ac555ca8f90196976324c7756504e7c9ee"); @@ -717,7 +707,6 @@ public void testVerifySpendProof() throws BadItemException, ZksnarkException { public void saplingBindingSig() throws BadItemException, ZksnarkException { long ctx = JLibrustzcash.librustzcashSaplingProvingCtxInit(); // generate spend proof - librustzcashInitZksnarkParams(); ZenTransactionBuilder builder = new ZenTransactionBuilder(wallet); SpendingKey sk = SpendingKey .decode("ff2c06269315333a9207f817d2eca0ac555ca8f90196976324c7756504e7c9ee"); @@ -756,7 +745,6 @@ public void pushShieldedTransaction() ContractExeException, AccountResourceInsufficientException, ZksnarkException { long ctx = JLibrustzcash.librustzcashSaplingProvingCtxInit(); // generate spend proof - librustzcashInitZksnarkParams(); dbManager.getDynamicPropertiesStore().saveAllowShieldedTransaction(1); dbManager.getDynamicPropertiesStore().saveTotalShieldedPoolValue(4010 * 1000000L); ZenTransactionBuilder builder = new ZenTransactionBuilder(wallet); @@ -789,7 +777,6 @@ public void pushShieldedTransaction() @Test public void finalCheck() throws BadItemException, ZksnarkException { long ctx = JLibrustzcash.librustzcashSaplingProvingCtxInit(); - librustzcashInitZksnarkParams(); ZenTransactionBuilder builder = new ZenTransactionBuilder(wallet); // generate spend proof SpendingKey sk = SpendingKey @@ -965,7 +952,6 @@ public void getSpendingKey() throws Exception { @Test public void testTwoCMWithDiffSkInOneTx() throws Exception { // generate spend proof - librustzcashInitZksnarkParams(); dbManager.getDynamicPropertiesStore().saveTotalShieldedPoolValue(110 * 1000000L); dbManager.getDynamicPropertiesStore().saveAllowShieldedTransaction(1); ZenTransactionBuilder builder = new ZenTransactionBuilder(wallet); @@ -1025,7 +1011,6 @@ private void executeTx(TransactionCapsule transactionCap) throws Exception { @Test public void testValueBalance() throws Exception { - librustzcashInitZksnarkParams(); dbManager.getDynamicPropertiesStore().saveAllowShieldedTransaction(1); //case 1, a public input, no input cm, an output cm, no public output { @@ -1246,7 +1231,6 @@ public void testValueBalance() throws Exception { @Test public void TestCreateMultipleTxAtTheSameTime() throws Exception { - librustzcashInitZksnarkParams(); dbManager.getDynamicPropertiesStore().saveAllowShieldedTransaction(1); List txList = Lists.newArrayList(); //case 1, a public input, no input cm, an output cm, no public output @@ -1364,7 +1348,6 @@ public void TestCreateMultipleTxAtTheSameTime() throws Exception { @Test public void TestCtxGeneratesTooMuchProof() throws Exception { - librustzcashInitZksnarkParams(); dbManager.getDynamicPropertiesStore().saveAllowShieldedTransaction(1); //case 3, no public input, an input cm, no output cm, a public output { @@ -1439,7 +1422,6 @@ public SpendDescriptionCapsule generateSpendProof(SpendDescriptionInfo spend, lo @Test public void TestGeneratesProofWithDiffCtx() throws Exception { - librustzcashInitZksnarkParams(); dbManager.getDynamicPropertiesStore().saveAllowShieldedTransaction(1); //case 3, no public input, an input cm, no output cm, a public output @@ -1498,7 +1480,6 @@ public SpendDescriptionCapsule generateSpendProof(SpendDescriptionInfo spend, lo @Test public void TestGeneratesProofWithWrongAlpha() throws Exception { - librustzcashInitZksnarkParams(); dbManager.getDynamicPropertiesStore().saveAllowShieldedTransaction(1); //case 3, no public input, an input cm, no output cm, a public output { @@ -1536,7 +1517,6 @@ public void TestGeneratesProofWithWrongAlpha() throws Exception { @Test public void TestGeneratesProofWithWrongRcm() throws Exception { long ctx = JLibrustzcash.librustzcashSaplingProvingCtxInit(); - librustzcashInitZksnarkParams(); ZenTransactionBuilder builder = new ZenTransactionBuilder(wallet); // generate spend proof SpendingKey sk = SpendingKey.random(); @@ -1557,7 +1537,6 @@ public void TestGeneratesProofWithWrongRcm() throws Exception { @Test public void TestWrongAsk() throws Exception { - librustzcashInitZksnarkParams(); dbManager.getDynamicPropertiesStore().saveAllowShieldedTransaction(1); //case 3, no public input, an input cm, no output cm, a public output @@ -1667,7 +1646,6 @@ private TransactionCapsule generateDefaultBuilder(ZenTransactionBuilder builder) @Test public void TestDefaultBuilder() throws Exception { - librustzcashInitZksnarkParams(); dbManager.getDynamicPropertiesStore().saveAllowShieldedTransaction(1); dbManager.getDynamicPropertiesStore().saveTotalShieldedPoolValue(1000 * 1000000L); @@ -1678,7 +1656,6 @@ public void TestDefaultBuilder() throws Exception { @Test public void TestWrongSpendRk() throws Exception { - librustzcashInitZksnarkParams(); dbManager.getDynamicPropertiesStore().saveAllowShieldedTransaction(1); { @@ -1716,7 +1693,6 @@ public SpendDescriptionCapsule generateSpendProof(SpendDescriptionInfo spend, lo @Test public void TestWrongSpendProof() throws Exception { - librustzcashInitZksnarkParams(); dbManager.getDynamicPropertiesStore().saveAllowShieldedTransaction(1); { @@ -1761,7 +1737,6 @@ public SpendDescriptionCapsule generateSpendProof(SpendDescriptionInfo spend, lo @Test public void TestWrongNf() throws Exception { - librustzcashInitZksnarkParams(); dbManager.getDynamicPropertiesStore().saveAllowShieldedTransaction(1); { @@ -1800,7 +1775,6 @@ public SpendDescriptionCapsule generateSpendProof(SpendDescriptionInfo spend, lo @Test public void TestWrongAnchor() throws Exception { - librustzcashInitZksnarkParams(); dbManager.getDynamicPropertiesStore().saveAllowShieldedTransaction(1); { ZenTransactionBuilder builder = new ZenTransactionBuilder(wallet) { diff --git a/framework/src/test/java/org/tron/core/zksnark/ShieldedReceiveTest.java b/framework/src/test/java/org/tron/core/zksnark/ShieldedReceiveTest.java index d1738dfa39b..4012029e8ae 100755 --- a/framework/src/test/java/org/tron/core/zksnark/ShieldedReceiveTest.java +++ b/framework/src/test/java/org/tron/core/zksnark/ShieldedReceiveTest.java @@ -17,6 +17,7 @@ import lombok.extern.slf4j.Slf4j; import org.junit.Assert; import org.junit.Before; +import org.junit.BeforeClass; import org.junit.Test; import org.tron.api.GrpcAPI.BytesMessage; import org.tron.api.GrpcAPI.DecryptNotes; @@ -129,7 +130,12 @@ public class ShieldedReceiveTest extends BaseTest { static { Args.setParam(new String[]{"--output-directory", dbPath()}, "config-localtest.conf"); ADDRESS_ONE_PRIVATE_KEY = getRandomPrivateKey(); - FROM_ADDRESS = getHexAddressByPrivateKey(ADDRESS_ONE_PRIVATE_KEY);; + FROM_ADDRESS = getHexAddressByPrivateKey(ADDRESS_ONE_PRIVATE_KEY); + } + + @BeforeClass + public static void initZksnarkParams() { + ZksnarkInitService.librustzcashInitZksnarkParams(); } /** @@ -145,10 +151,6 @@ public void init() { init = true; } - private static void librustzcashInitZksnarkParams() { - ZksnarkInitService.librustzcashInitZksnarkParams(); - } - private static byte[] randomUint256() { return org.tron.keystore.Wallet.generateRandomBytes(32); } @@ -244,7 +246,6 @@ public void testSetShieldedTransactionFee() { */ @Test public void testCreateBeforeAllowZksnark() throws ZksnarkException { - librustzcashInitZksnarkParams(); chainBaseManager.getDynamicPropertiesStore().saveAllowShieldedTransaction(0); createCapsule(); ZenTransactionBuilder builder = new ZenTransactionBuilder(wallet); @@ -284,7 +285,6 @@ public void testCreateBeforeAllowZksnark() throws ZksnarkException { @Test public void testBroadcastBeforeAllowZksnark() throws ZksnarkException, SignatureFormatException, SignatureException, PermissionException { - librustzcashInitZksnarkParams(); chainBaseManager.getDynamicPropertiesStore().saveAllowShieldedTransaction(0);// or 1 createCapsule(); ZenTransactionBuilder builder = new ZenTransactionBuilder(wallet); @@ -309,13 +309,10 @@ public void testBroadcastBeforeAllowZksnark() transactionCap = TransactionUtils.addTransactionSign(transactionCap.getInstance(), ADDRESS_ONE_PRIVATE_KEY, chainBaseManager.getAccountStore()); try { - dbManager.pushTransaction(transactionCap); - Assert.assertFalse(true); + boolean res = dbManager.pushTransaction(transactionCap); + Assert.assertFalse(res); } catch (Exception e) { - Assert.assertTrue(e instanceof ContractValidateException); - Assert.assertEquals( - "Not support Shielded Transaction, need to be opened by the committee", - e.getMessage()); + Assert.fail(e.getMessage()); } } @@ -324,7 +321,6 @@ public void testBroadcastBeforeAllowZksnark() * outputproof example dynamically according to the params file */ public String[] generateSpendAndOutputParams() throws ZksnarkException, BadItemException { - librustzcashInitZksnarkParams(); chainBaseManager.getDynamicPropertiesStore().saveAllowShieldedTransaction(1); chainBaseManager.getDynamicPropertiesStore().saveTotalShieldedPoolValue(100 * 1000000L); ZenTransactionBuilder builder = new ZenTransactionBuilder(wallet); @@ -461,7 +457,6 @@ private long benchmarkVerifyOutput(String outputParams) throws ZksnarkException @Test public void calBenchmarkVerifySpend() throws ZksnarkException, BadItemException { - librustzcashInitZksnarkParams(); System.out.println("--- load ok ---"); int count = 10; @@ -493,7 +488,6 @@ public void calBenchmarkVerifySpend() throws ZksnarkException, BadItemException @Test public void calBenchmarkVerifyOutput() throws ZksnarkException, BadItemException { - librustzcashInitZksnarkParams(); System.out.println("--- load ok ---"); int count = 2; @@ -589,7 +583,6 @@ private ZenTransactionBuilder generateBuilderWithoutColumnInDescription( @Test public void testReceiveDescriptionWithEmptyCv() throws BadItemException, RuntimeException, ZksnarkException { - librustzcashInitZksnarkParams(); chainBaseManager.getDynamicPropertiesStore().saveAllowShieldedTransaction(1); chainBaseManager.getDynamicPropertiesStore().saveTotalShieldedPoolValue(100 * 1000000L); ZenTransactionBuilder builder = new ZenTransactionBuilder(wallet); @@ -635,7 +628,6 @@ public void testReceiveDescriptionWithEmptyCv() @Test public void testReceiveDescriptionWithEmptyCm() throws BadItemException, RuntimeException, ZksnarkException { - librustzcashInitZksnarkParams(); chainBaseManager.getDynamicPropertiesStore().saveAllowShieldedTransaction(1); chainBaseManager.getDynamicPropertiesStore().saveTotalShieldedPoolValue(100 * 1000000L); ZenTransactionBuilder builder = new ZenTransactionBuilder(wallet); @@ -678,7 +670,6 @@ public void testReceiveDescriptionWithEmptyCm() @Test public void testReceiveDescriptionWithEmptyEpk() throws BadItemException, RuntimeException, ZksnarkException { - librustzcashInitZksnarkParams(); chainBaseManager.getDynamicPropertiesStore().saveAllowShieldedTransaction(1); chainBaseManager.getDynamicPropertiesStore().saveTotalShieldedPoolValue(100 * 1000000L); ZenTransactionBuilder builder = new ZenTransactionBuilder(wallet); @@ -720,7 +711,6 @@ public void testReceiveDescriptionWithEmptyEpk() @Test public void testReceiveDescriptionWithEmptyZkproof() throws BadItemException, RuntimeException, ZksnarkException { - librustzcashInitZksnarkParams(); chainBaseManager.getDynamicPropertiesStore().saveAllowShieldedTransaction(1); chainBaseManager.getDynamicPropertiesStore().saveTotalShieldedPoolValue(100 * 1000000L); ZenTransactionBuilder builder = new ZenTransactionBuilder(wallet); @@ -763,7 +753,6 @@ public void testReceiveDescriptionWithEmptyZkproof() @Test public void testReceiveDescriptionWithEmptyCenc() throws BadItemException, RuntimeException, ZksnarkException { - librustzcashInitZksnarkParams(); dbManager.getDynamicPropertiesStore().saveAllowShieldedTransaction(1); dbManager.getDynamicPropertiesStore().saveTotalShieldedPoolValue(100 * 1000000L); ZenTransactionBuilder builder = new ZenTransactionBuilder(wallet); @@ -806,7 +795,6 @@ public void testReceiveDescriptionWithEmptyCenc() @Test public void testReceiveDescriptionWithEmptyCout() throws BadItemException, RuntimeException, ZksnarkException { - librustzcashInitZksnarkParams(); chainBaseManager.getDynamicPropertiesStore().saveAllowShieldedTransaction(1); chainBaseManager.getDynamicPropertiesStore().saveTotalShieldedPoolValue(100 * 1000000L); ZenTransactionBuilder builder = new ZenTransactionBuilder(wallet); @@ -1064,7 +1052,6 @@ private ZenTransactionBuilder generateBuilder(ZenTransactionBuilder builder, lon @Test public void testReceiveDescriptionWithWrongCv() throws BadItemException, RuntimeException, ZksnarkException { - librustzcashInitZksnarkParams(); chainBaseManager.getDynamicPropertiesStore().saveAllowShieldedTransaction(1); chainBaseManager.getDynamicPropertiesStore().saveTotalShieldedPoolValue(100 * 1000000L); ZenTransactionBuilder builder = new ZenTransactionBuilder(wallet); @@ -1093,7 +1080,6 @@ public void testReceiveDescriptionWithWrongCv() @Test public void testReceiveDescriptionWithWrongZkproof() throws BadItemException, RuntimeException, ZksnarkException { - librustzcashInitZksnarkParams(); chainBaseManager.getDynamicPropertiesStore().saveAllowShieldedTransaction(1); chainBaseManager.getDynamicPropertiesStore().saveTotalShieldedPoolValue(100 * 1000000L); ZenTransactionBuilder builder = new ZenTransactionBuilder(wallet); @@ -1122,7 +1108,6 @@ public void testReceiveDescriptionWithWrongZkproof() @Test public void testReceiveDescriptionWithWrongD() throws BadItemException, RuntimeException, ZksnarkException { - librustzcashInitZksnarkParams(); chainBaseManager.getDynamicPropertiesStore().saveAllowShieldedTransaction(1); chainBaseManager.getDynamicPropertiesStore().saveTotalShieldedPoolValue(100 * 1000000L); ZenTransactionBuilder builder = new ZenTransactionBuilder(wallet); @@ -1153,7 +1138,6 @@ public void testReceiveDescriptionWithWrongD() @Test public void testReceiveDescriptionWithWrongPkd() throws BadItemException, RuntimeException, ZksnarkException { - librustzcashInitZksnarkParams(); chainBaseManager.getDynamicPropertiesStore().saveAllowShieldedTransaction(1); chainBaseManager.getDynamicPropertiesStore().saveTotalShieldedPoolValue(100 * 1000000L); ZenTransactionBuilder builder = new ZenTransactionBuilder(wallet); @@ -1184,7 +1168,6 @@ public void testReceiveDescriptionWithWrongPkd() @Test public void testReceiveDescriptionWithWrongValue() throws BadItemException, RuntimeException, ZksnarkException { - librustzcashInitZksnarkParams(); chainBaseManager.getDynamicPropertiesStore().saveAllowShieldedTransaction(1); chainBaseManager.getDynamicPropertiesStore().saveTotalShieldedPoolValue(100 * 1000000L); ZenTransactionBuilder builder = new ZenTransactionBuilder(wallet); @@ -1213,7 +1196,6 @@ public void testReceiveDescriptionWithWrongValue() @Test public void testReceiveDescriptionWithWrongRcm() throws BadItemException, RuntimeException, ZksnarkException { - librustzcashInitZksnarkParams(); chainBaseManager.getDynamicPropertiesStore().saveAllowShieldedTransaction(1); chainBaseManager.getDynamicPropertiesStore().saveTotalShieldedPoolValue(100 * 1000000L); ZenTransactionBuilder builder = new ZenTransactionBuilder(wallet); @@ -1242,7 +1224,6 @@ public void testReceiveDescriptionWithWrongRcm() @Test public void testNotMatchAskAndNsk() throws BadItemException, RuntimeException, ZksnarkException { - librustzcashInitZksnarkParams(); chainBaseManager.getDynamicPropertiesStore().saveAllowShieldedTransaction(1); chainBaseManager.getDynamicPropertiesStore().saveTotalShieldedPoolValue(100 * 1000000L); ZenTransactionBuilder builder = new ZenTransactionBuilder(wallet); @@ -1293,7 +1274,6 @@ public void testNotMatchAskAndNsk() @Test public void testRandomOvk() throws BadItemException, RuntimeException, ZksnarkException { - librustzcashInitZksnarkParams(); chainBaseManager.getDynamicPropertiesStore().saveAllowShieldedTransaction(1); chainBaseManager.getDynamicPropertiesStore().saveTotalShieldedPoolValue(100 * 1000000L); ZenTransactionBuilder builder = new ZenTransactionBuilder(wallet); @@ -1337,7 +1317,6 @@ public void testRandomOvk() //@Test not used public void testSameInputCm() throws BadItemException, RuntimeException, ZksnarkException { - librustzcashInitZksnarkParams(); chainBaseManager.getDynamicPropertiesStore().saveAllowShieldedTransaction(1); chainBaseManager.getDynamicPropertiesStore().saveTotalShieldedPoolValue(100 * 1000000L); ZenTransactionBuilder builder = new ZenTransactionBuilder(wallet); @@ -1386,7 +1365,6 @@ public void testSameInputCm() @Test public void testSameOutputCm() throws BadItemException, RuntimeException, ZksnarkException { - librustzcashInitZksnarkParams(); chainBaseManager.getDynamicPropertiesStore().saveAllowShieldedTransaction(1); chainBaseManager.getDynamicPropertiesStore().saveTotalShieldedPoolValue(100 * 1000000L); ZenTransactionBuilder builder = new ZenTransactionBuilder(wallet); @@ -1438,7 +1416,6 @@ public void testSameOutputCm() @Test public void testShieldInputInsufficient() throws BadItemException, RuntimeException, ZksnarkException { - librustzcashInitZksnarkParams(); chainBaseManager.getDynamicPropertiesStore().saveAllowShieldedTransaction(1); chainBaseManager.getDynamicPropertiesStore().saveTotalShieldedPoolValue(100 * 1000000L); ZenTransactionBuilder builder = new ZenTransactionBuilder(wallet); @@ -1483,7 +1460,6 @@ public void testShieldInputInsufficient() */ @Test public void testTransparentInputInsufficient() throws RuntimeException, ZksnarkException { - librustzcashInitZksnarkParams(); chainBaseManager.getDynamicPropertiesStore().saveAllowShieldedTransaction(1); ZenTransactionBuilder builder = new ZenTransactionBuilder(wallet); long ctx = JLibrustzcash.librustzcashSaplingProvingCtxInit(); @@ -1732,7 +1708,6 @@ public TransactionCapsule generateTransactionCapsule(ZenTransactionBuilder build public void testSignWithoutFromAddress() throws BadItemException, ContractValidateException, RuntimeException, ZksnarkException { - librustzcashInitZksnarkParams(); chainBaseManager.getDynamicPropertiesStore().saveAllowShieldedTransaction(1); chainBaseManager.getDynamicPropertiesStore().saveTotalShieldedPoolValue(100 * 1000000L); ZenTransactionBuilder builder = new ZenTransactionBuilder(wallet); @@ -1772,7 +1747,6 @@ public void testSignWithoutFromAddress() public void testSignWithoutFromAmout() throws BadItemException, ContractValidateException, RuntimeException, ZksnarkException { - librustzcashInitZksnarkParams(); chainBaseManager.getDynamicPropertiesStore().saveAllowShieldedTransaction(1); chainBaseManager.getDynamicPropertiesStore().saveTotalShieldedPoolValue(100 * 1000000L); ZenTransactionBuilder builder = new ZenTransactionBuilder(wallet); @@ -1811,7 +1785,6 @@ public void testSignWithoutFromAmout() @Test public void testSignWithoutSpendDescription() throws BadItemException, RuntimeException, ZksnarkException { - librustzcashInitZksnarkParams(); chainBaseManager.getDynamicPropertiesStore().saveAllowShieldedTransaction(1); chainBaseManager.getDynamicPropertiesStore().saveTotalShieldedPoolValue(100 * 1000000L); ZenTransactionBuilder builder = new ZenTransactionBuilder(wallet); @@ -1857,7 +1830,6 @@ public void testSignWithoutSpendDescription() @Test public void testSignWithoutReceiveDescription() throws BadItemException, RuntimeException, ZksnarkException { - librustzcashInitZksnarkParams(); chainBaseManager.getDynamicPropertiesStore().saveAllowShieldedTransaction(1); chainBaseManager.getDynamicPropertiesStore().saveTotalShieldedPoolValue(100 * 1000000L); ZenTransactionBuilder builder = new ZenTransactionBuilder(wallet); @@ -1904,7 +1876,6 @@ public void testSignWithoutReceiveDescription() public void testSignWithoutToAddress() throws BadItemException, ContractValidateException, RuntimeException, ZksnarkException { - librustzcashInitZksnarkParams(); chainBaseManager.getDynamicPropertiesStore().saveAllowShieldedTransaction(1); chainBaseManager.getDynamicPropertiesStore().saveTotalShieldedPoolValue(100 * 1000000L); ZenTransactionBuilder builder = new ZenTransactionBuilder(wallet); @@ -1944,7 +1915,6 @@ public void testSignWithoutToAddress() public void testSignWithoutToAmount() throws BadItemException, ContractValidateException, RuntimeException, ZksnarkException { - librustzcashInitZksnarkParams(); chainBaseManager.getDynamicPropertiesStore().saveAllowShieldedTransaction(1); chainBaseManager.getDynamicPropertiesStore().saveTotalShieldedPoolValue(100 * 1000000L); ZenTransactionBuilder builder = new ZenTransactionBuilder(wallet); @@ -1983,7 +1953,6 @@ public void testSignWithoutToAmount() @Test public void testSpendSignatureWithWrongColumn() throws BadItemException, RuntimeException, ZksnarkException { - librustzcashInitZksnarkParams(); chainBaseManager.getDynamicPropertiesStore().saveAllowShieldedTransaction(1); chainBaseManager.getDynamicPropertiesStore().saveTotalShieldedPoolValue(100 * 1000000L); ZenTransactionBuilder builder = new ZenTransactionBuilder(wallet); @@ -2101,7 +2070,6 @@ public void testSpendSignatureWithWrongColumn() @Test public void testIsolateSignature() throws ZksnarkException, BadItemException, ContractValidateException, ContractExeException { - librustzcashInitZksnarkParams(); chainBaseManager.getDynamicPropertiesStore().saveAllowShieldedTransaction(1); chainBaseManager.getDynamicPropertiesStore().saveTotalShieldedPoolValue(100 * 1000000L); ZenTransactionBuilder builder = new ZenTransactionBuilder(wallet); @@ -2236,7 +2204,6 @@ public void testMemoTooLong() throws ContractValidateException, TooBigTransactio AccountResourceInsufficientException, InvalidProtocolBufferException, ZksnarkException { long ctx = JLibrustzcash.librustzcashSaplingProvingCtxInit(); - librustzcashInitZksnarkParams(); chainBaseManager.getDynamicPropertiesStore().saveAllowShieldedTransaction(1); chainBaseManager.getDynamicPropertiesStore().saveTotalShieldedPoolValue(100 * 1000000L); ZenTransactionBuilder builder = new ZenTransactionBuilder(wallet); @@ -2319,7 +2286,6 @@ public void testMemoNotEnough() throws ContractValidateException, TooBigTransact AccountResourceInsufficientException, InvalidProtocolBufferException, ZksnarkException { long ctx = JLibrustzcash.librustzcashSaplingProvingCtxInit(); - librustzcashInitZksnarkParams(); chainBaseManager.getDynamicPropertiesStore().saveAllowShieldedTransaction(1); chainBaseManager.getDynamicPropertiesStore().saveTotalShieldedPoolValue(100 * 1000000L); ZenTransactionBuilder builder = new ZenTransactionBuilder(wallet); @@ -2413,7 +2379,6 @@ public void pushSameSkAndScanAndSpend() throws Exception { dbManager.pushBlock(new BlockCapsule(block)); //create transactions - librustzcashInitZksnarkParams(); chainBaseManager.getDynamicPropertiesStore().saveAllowShieldedTransaction(1); chainBaseManager.getDynamicPropertiesStore().saveTotalShieldedPoolValue(1000 * 1000000L); ZenTransactionBuilder builder = new ZenTransactionBuilder(wallet); diff --git a/framework/src/test/resources/config-localtest.conf b/framework/src/test/resources/config-localtest.conf index ff31369a915..50a3f97d8b7 100644 --- a/framework/src/test/resources/config-localtest.conf +++ b/framework/src/test/resources/config-localtest.conf @@ -90,7 +90,7 @@ node { minParticipationRate = 0 - fullNodeAllowShieldedTransaction = true + allowShieldedTransactionApi = true zenTokenId = 1000001 From 6a75d5011745fccdb8a206d6971e4542f96af71a Mon Sep 17 00:00:00 2001 From: bladehan1 Date: Thu, 14 Aug 2025 16:33:21 +0800 Subject: [PATCH 26/59] feat(section-bloom): write section-bloom and block.bloom (#6372) --- framework/src/main/java/org/tron/core/db/Manager.java | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/framework/src/main/java/org/tron/core/db/Manager.java b/framework/src/main/java/org/tron/core/db/Manager.java index 81c4a941628..17e2687e98b 100644 --- a/framework/src/main/java/org/tron/core/db/Manager.java +++ b/framework/src/main/java/org/tron/core/db/Manager.java @@ -1857,12 +1857,10 @@ private void processBlock(BlockCapsule block, List txs) chainBaseManager.getBalanceTraceStore().resetCurrentBlockTrace(); - if (CommonParameter.getInstance().isJsonRpcFilterEnabled()) { - Bloom blockBloom = chainBaseManager.getSectionBloomStore() - .initBlockSection(transactionRetCapsule); - chainBaseManager.getSectionBloomStore().write(block.getNum()); - block.setBloom(blockBloom); - } + Bloom blockBloom = chainBaseManager.getSectionBloomStore() + .initBlockSection(transactionRetCapsule); + chainBaseManager.getSectionBloomStore().write(block.getNum()); + block.setBloom(blockBloom); } private void payReward(BlockCapsule block) { From c7f133de6a2f86992bdc82459646612b29cc81cb Mon Sep 17 00:00:00 2001 From: 0xbigapple Date: Fri, 15 Aug 2025 10:03:13 +0800 Subject: [PATCH 27/59] feat(jsonrpc): jsonrpc set error resolver (#6369) * JsonRpc:setTronErrorResolver * fix: add resData.length check while throw JsonRpcInternalException * add data field in TronException * feat(api): rename JsonRpcErrorResolver, add unit test * feat(api): add note and test * feat(api): add JsonRpcException.java * feat(api): remove logs and unnecessary test case * feat(api): enhance estimateGas error handling to include data field * refactor: optimize JsonRpcErrorResolver error data resolution --- .../java/org/tron/common/utils/ByteArray.java | 2 +- .../exception/jsonrpc/JsonRpcException.java | 32 ++++++++ .../JsonRpcInternalException.java | 8 +- .../JsonRpcInvalidParamsException.java | 4 +- .../JsonRpcInvalidRequestException.java | 4 +- .../JsonRpcMethodNotFoundException.java | 4 +- .../JsonRpcTooManyResultException.java | 4 +- .../src/main/java/org/tron/core/Wallet.java | 2 +- .../core/services/jsonrpc/JsonRpcApiUtil.java | 2 +- .../jsonrpc/JsonRpcErrorResolver.java | 81 +++++++++++++++++++ .../core/services/jsonrpc/JsonRpcServlet.java | 1 + .../core/services/jsonrpc/TronJsonRpc.java | 10 +-- .../services/jsonrpc/TronJsonRpcImpl.java | 25 ++++-- .../jsonrpc/filters/LogBlockQuery.java | 2 +- .../services/jsonrpc/filters/LogFilter.java | 2 +- .../jsonrpc/filters/LogFilterAndResult.java | 2 +- .../jsonrpc/filters/LogFilterWrapper.java | 2 +- .../services/jsonrpc/filters/LogMatch.java | 2 +- .../jsonrpc/types/BuildArguments.java | 4 +- .../services/jsonrpc/types/CallArguments.java | 4 +- .../tron/common/runtime/vm/Create2Test.java | 2 +- .../java/org/tron/core/CoreExceptionTest.java | 10 +-- .../org/tron/core/jsonrpc/JsonRpcTest.java | 2 +- .../tron/core/jsonrpc/JsonrpcServiceTest.java | 2 +- .../core/jsonrpc/LogMatchExactlyTest.java | 2 +- .../services/jsonrpc/BuildArgumentsTest.java | 4 +- .../services/jsonrpc/CallArgumentsTest.java | 4 +- .../jsonrpc/JsonRpcErrorResolverTest.java | 75 +++++++++++++++++ 28 files changed, 250 insertions(+), 48 deletions(-) create mode 100644 common/src/main/java/org/tron/core/exception/jsonrpc/JsonRpcException.java rename common/src/main/java/org/tron/core/exception/{ => jsonrpc}/JsonRpcInternalException.java (53%) rename common/src/main/java/org/tron/core/exception/{ => jsonrpc}/JsonRpcInvalidParamsException.java (68%) rename common/src/main/java/org/tron/core/exception/{ => jsonrpc}/JsonRpcInvalidRequestException.java (69%) rename common/src/main/java/org/tron/core/exception/{ => jsonrpc}/JsonRpcMethodNotFoundException.java (68%) rename common/src/main/java/org/tron/core/exception/{ => jsonrpc}/JsonRpcTooManyResultException.java (69%) create mode 100644 framework/src/main/java/org/tron/core/services/jsonrpc/JsonRpcErrorResolver.java create mode 100644 framework/src/test/java/org/tron/core/services/jsonrpc/JsonRpcErrorResolverTest.java diff --git a/common/src/main/java/org/tron/common/utils/ByteArray.java b/common/src/main/java/org/tron/common/utils/ByteArray.java index b77dd310380..d0ac4cbaddf 100644 --- a/common/src/main/java/org/tron/common/utils/ByteArray.java +++ b/common/src/main/java/org/tron/common/utils/ByteArray.java @@ -14,7 +14,7 @@ import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; import org.bouncycastle.util.encoders.Hex; -import org.tron.core.exception.JsonRpcInvalidParamsException; +import org.tron.core.exception.jsonrpc.JsonRpcInvalidParamsException; /* * Copyright (c) [2016] [ ] diff --git a/common/src/main/java/org/tron/core/exception/jsonrpc/JsonRpcException.java b/common/src/main/java/org/tron/core/exception/jsonrpc/JsonRpcException.java new file mode 100644 index 00000000000..7ed42d101d5 --- /dev/null +++ b/common/src/main/java/org/tron/core/exception/jsonrpc/JsonRpcException.java @@ -0,0 +1,32 @@ +package org.tron.core.exception.jsonrpc; + +import lombok.Getter; +import org.tron.core.exception.TronException; + +@Getter +public class JsonRpcException extends TronException { + private Object data = null; + + public JsonRpcException() { + super(); + report(); + } + + public JsonRpcException(String message, Object data) { + super(message); + this.data = data; + report(); + } + + public JsonRpcException(String message) { + super(message); + report(); + } + + public JsonRpcException(String message, Throwable cause) { + super(message, cause); + report(); + } + + +} diff --git a/common/src/main/java/org/tron/core/exception/JsonRpcInternalException.java b/common/src/main/java/org/tron/core/exception/jsonrpc/JsonRpcInternalException.java similarity index 53% rename from common/src/main/java/org/tron/core/exception/JsonRpcInternalException.java rename to common/src/main/java/org/tron/core/exception/jsonrpc/JsonRpcInternalException.java index 12310e67747..904449866ae 100644 --- a/common/src/main/java/org/tron/core/exception/JsonRpcInternalException.java +++ b/common/src/main/java/org/tron/core/exception/jsonrpc/JsonRpcInternalException.java @@ -1,6 +1,6 @@ -package org.tron.core.exception; +package org.tron.core.exception.jsonrpc; -public class JsonRpcInternalException extends TronException { +public class JsonRpcInternalException extends JsonRpcException { public JsonRpcInternalException() { super(); @@ -13,4 +13,8 @@ public JsonRpcInternalException(String message) { public JsonRpcInternalException(String message, Throwable cause) { super(message, cause); } + + public JsonRpcInternalException(String message, Object data) { + super(message, data); + } } \ No newline at end of file diff --git a/common/src/main/java/org/tron/core/exception/JsonRpcInvalidParamsException.java b/common/src/main/java/org/tron/core/exception/jsonrpc/JsonRpcInvalidParamsException.java similarity index 68% rename from common/src/main/java/org/tron/core/exception/JsonRpcInvalidParamsException.java rename to common/src/main/java/org/tron/core/exception/jsonrpc/JsonRpcInvalidParamsException.java index adf205a309a..55ee15521e1 100644 --- a/common/src/main/java/org/tron/core/exception/JsonRpcInvalidParamsException.java +++ b/common/src/main/java/org/tron/core/exception/jsonrpc/JsonRpcInvalidParamsException.java @@ -1,6 +1,6 @@ -package org.tron.core.exception; +package org.tron.core.exception.jsonrpc; -public class JsonRpcInvalidParamsException extends TronException { +public class JsonRpcInvalidParamsException extends JsonRpcException { public JsonRpcInvalidParamsException() { super(); diff --git a/common/src/main/java/org/tron/core/exception/JsonRpcInvalidRequestException.java b/common/src/main/java/org/tron/core/exception/jsonrpc/JsonRpcInvalidRequestException.java similarity index 69% rename from common/src/main/java/org/tron/core/exception/JsonRpcInvalidRequestException.java rename to common/src/main/java/org/tron/core/exception/jsonrpc/JsonRpcInvalidRequestException.java index 2689bff0cd2..32bd11a3ed9 100644 --- a/common/src/main/java/org/tron/core/exception/JsonRpcInvalidRequestException.java +++ b/common/src/main/java/org/tron/core/exception/jsonrpc/JsonRpcInvalidRequestException.java @@ -1,6 +1,6 @@ -package org.tron.core.exception; +package org.tron.core.exception.jsonrpc; -public class JsonRpcInvalidRequestException extends TronException { +public class JsonRpcInvalidRequestException extends JsonRpcException { public JsonRpcInvalidRequestException() { super(); diff --git a/common/src/main/java/org/tron/core/exception/JsonRpcMethodNotFoundException.java b/common/src/main/java/org/tron/core/exception/jsonrpc/JsonRpcMethodNotFoundException.java similarity index 68% rename from common/src/main/java/org/tron/core/exception/JsonRpcMethodNotFoundException.java rename to common/src/main/java/org/tron/core/exception/jsonrpc/JsonRpcMethodNotFoundException.java index d8e18168d9d..406a51f45bd 100644 --- a/common/src/main/java/org/tron/core/exception/JsonRpcMethodNotFoundException.java +++ b/common/src/main/java/org/tron/core/exception/jsonrpc/JsonRpcMethodNotFoundException.java @@ -1,6 +1,6 @@ -package org.tron.core.exception; +package org.tron.core.exception.jsonrpc; -public class JsonRpcMethodNotFoundException extends TronException { +public class JsonRpcMethodNotFoundException extends JsonRpcException { public JsonRpcMethodNotFoundException() { super(); diff --git a/common/src/main/java/org/tron/core/exception/JsonRpcTooManyResultException.java b/common/src/main/java/org/tron/core/exception/jsonrpc/JsonRpcTooManyResultException.java similarity index 69% rename from common/src/main/java/org/tron/core/exception/JsonRpcTooManyResultException.java rename to common/src/main/java/org/tron/core/exception/jsonrpc/JsonRpcTooManyResultException.java index e8e183d49c1..03bc089b1c7 100644 --- a/common/src/main/java/org/tron/core/exception/JsonRpcTooManyResultException.java +++ b/common/src/main/java/org/tron/core/exception/jsonrpc/JsonRpcTooManyResultException.java @@ -1,6 +1,6 @@ -package org.tron.core.exception; +package org.tron.core.exception.jsonrpc; -public class JsonRpcTooManyResultException extends TronException { +public class JsonRpcTooManyResultException extends JsonRpcException { public JsonRpcTooManyResultException() { super(); diff --git a/framework/src/main/java/org/tron/core/Wallet.java b/framework/src/main/java/org/tron/core/Wallet.java index f1710146b72..125443d59dd 100755 --- a/framework/src/main/java/org/tron/core/Wallet.java +++ b/framework/src/main/java/org/tron/core/Wallet.java @@ -177,7 +177,6 @@ import org.tron.core.exception.DupTransactionException; import org.tron.core.exception.HeaderNotFound; import org.tron.core.exception.ItemNotFoundException; -import org.tron.core.exception.JsonRpcInvalidParamsException; import org.tron.core.exception.NonUniqueObjectException; import org.tron.core.exception.PermissionException; import org.tron.core.exception.SignatureFormatException; @@ -188,6 +187,7 @@ import org.tron.core.exception.VMIllegalException; import org.tron.core.exception.ValidateSignatureException; import org.tron.core.exception.ZksnarkException; +import org.tron.core.exception.jsonrpc.JsonRpcInvalidParamsException; import org.tron.core.net.TronNetDelegate; import org.tron.core.net.TronNetService; import org.tron.core.net.message.adv.TransactionMessage; diff --git a/framework/src/main/java/org/tron/core/services/jsonrpc/JsonRpcApiUtil.java b/framework/src/main/java/org/tron/core/services/jsonrpc/JsonRpcApiUtil.java index 955ba55060f..4a60f14b534 100644 --- a/framework/src/main/java/org/tron/core/services/jsonrpc/JsonRpcApiUtil.java +++ b/framework/src/main/java/org/tron/core/services/jsonrpc/JsonRpcApiUtil.java @@ -26,7 +26,7 @@ import org.tron.common.utils.Sha256Hash; import org.tron.common.utils.StringUtil; import org.tron.core.Wallet; -import org.tron.core.exception.JsonRpcInvalidParamsException; +import org.tron.core.exception.jsonrpc.JsonRpcInvalidParamsException; import org.tron.protos.Protocol.Block; import org.tron.protos.Protocol.Transaction; import org.tron.protos.Protocol.Transaction.Contract.ContractType; diff --git a/framework/src/main/java/org/tron/core/services/jsonrpc/JsonRpcErrorResolver.java b/framework/src/main/java/org/tron/core/services/jsonrpc/JsonRpcErrorResolver.java new file mode 100644 index 00000000000..b92b3cf1af6 --- /dev/null +++ b/framework/src/main/java/org/tron/core/services/jsonrpc/JsonRpcErrorResolver.java @@ -0,0 +1,81 @@ +package org.tron.core.services.jsonrpc; + +import com.fasterxml.jackson.databind.JsonNode; +import com.googlecode.jsonrpc4j.ErrorData; +import com.googlecode.jsonrpc4j.ErrorResolver; +import com.googlecode.jsonrpc4j.JsonRpcError; +import com.googlecode.jsonrpc4j.JsonRpcErrors; +import com.googlecode.jsonrpc4j.ReflectionUtil; +import java.lang.reflect.Method; +import java.util.List; +import org.tron.core.exception.jsonrpc.JsonRpcException; + +/** + * {@link ErrorResolver} that uses annotations. + */ +public enum JsonRpcErrorResolver implements ErrorResolver { + INSTANCE; + + /** + * {@inheritDoc} + */ + @Override + public JsonError resolveError( + Throwable thrownException, Method method, List arguments) { + JsonRpcError resolver = getResolverForException(thrownException, method); + if (notFoundResolver(resolver)) { + return null; + } + + String message = hasErrorMessage(resolver) ? resolver.message() : thrownException.getMessage(); + + // data priority: exception > annotation > default ErrorData + Object data = null; + if (thrownException instanceof JsonRpcException) { + JsonRpcException jsonRpcException = (JsonRpcException) thrownException; + data = jsonRpcException.getData(); + } + + if (data == null) { + data = hasErrorData(resolver) + ? resolver.data() + : new ErrorData(resolver.exception().getName(), message); + } + + return new JsonError(resolver.code(), message, data); + } + + private JsonRpcError getResolverForException(Throwable thrownException, Method method) { + JsonRpcErrors errors = ReflectionUtil.getAnnotation(method, JsonRpcErrors.class); + if (hasAnnotations(errors)) { + for (JsonRpcError errorDefined : errors.value()) { + if (isExceptionInstanceOfError(thrownException, errorDefined)) { + return errorDefined; + } + } + } + return null; + } + + private boolean notFoundResolver(JsonRpcError resolver) { + return resolver == null; + } + + private boolean hasErrorMessage(JsonRpcError em) { + // noinspection ConstantConditions + return em.message() != null && !em.message().trim().isEmpty(); + } + + private boolean hasErrorData(JsonRpcError em) { + // noinspection ConstantConditions + return em.data() != null && !em.data().trim().isEmpty(); + } + + private boolean hasAnnotations(JsonRpcErrors errors) { + return errors != null; + } + + private boolean isExceptionInstanceOfError(Throwable target, JsonRpcError em) { + return em.exception().isInstance(target); + } +} \ No newline at end of file diff --git a/framework/src/main/java/org/tron/core/services/jsonrpc/JsonRpcServlet.java b/framework/src/main/java/org/tron/core/services/jsonrpc/JsonRpcServlet.java index 878b71d86b5..104a0e9e470 100644 --- a/framework/src/main/java/org/tron/core/services/jsonrpc/JsonRpcServlet.java +++ b/framework/src/main/java/org/tron/core/services/jsonrpc/JsonRpcServlet.java @@ -43,6 +43,7 @@ public void init(ServletConfig config) throws ServletException { true); rpcServer = new JsonRpcServer(compositeService); + rpcServer.setErrorResolver(JsonRpcErrorResolver.INSTANCE); HttpStatusCodeProvider httpStatusCodeProvider = new HttpStatusCodeProvider() { @Override diff --git a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpc.java b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpc.java index 52a3a2380d1..c078cecdb66 100644 --- a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpc.java +++ b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpc.java @@ -18,11 +18,11 @@ import org.tron.common.utils.ByteArray; import org.tron.core.exception.BadItemException; import org.tron.core.exception.ItemNotFoundException; -import org.tron.core.exception.JsonRpcInternalException; -import org.tron.core.exception.JsonRpcInvalidParamsException; -import org.tron.core.exception.JsonRpcInvalidRequestException; -import org.tron.core.exception.JsonRpcMethodNotFoundException; -import org.tron.core.exception.JsonRpcTooManyResultException; +import org.tron.core.exception.jsonrpc.JsonRpcInternalException; +import org.tron.core.exception.jsonrpc.JsonRpcInvalidParamsException; +import org.tron.core.exception.jsonrpc.JsonRpcInvalidRequestException; +import org.tron.core.exception.jsonrpc.JsonRpcMethodNotFoundException; +import org.tron.core.exception.jsonrpc.JsonRpcTooManyResultException; import org.tron.core.services.jsonrpc.types.BlockResult; import org.tron.core.services.jsonrpc.types.BuildArguments; import org.tron.core.services.jsonrpc.types.CallArguments; diff --git a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java index afc401e7e7c..40c5f7713ae 100644 --- a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java +++ b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java @@ -56,12 +56,12 @@ import org.tron.core.exception.ContractValidateException; import org.tron.core.exception.HeaderNotFound; import org.tron.core.exception.ItemNotFoundException; -import org.tron.core.exception.JsonRpcInternalException; -import org.tron.core.exception.JsonRpcInvalidParamsException; -import org.tron.core.exception.JsonRpcInvalidRequestException; -import org.tron.core.exception.JsonRpcMethodNotFoundException; -import org.tron.core.exception.JsonRpcTooManyResultException; import org.tron.core.exception.VMIllegalException; +import org.tron.core.exception.jsonrpc.JsonRpcInternalException; +import org.tron.core.exception.jsonrpc.JsonRpcInvalidParamsException; +import org.tron.core.exception.jsonrpc.JsonRpcInvalidRequestException; +import org.tron.core.exception.jsonrpc.JsonRpcMethodNotFoundException; +import org.tron.core.exception.jsonrpc.JsonRpcTooManyResultException; import org.tron.core.services.NodeInfoService; import org.tron.core.services.http.JsonFormat; import org.tron.core.services.http.Util; @@ -468,7 +468,6 @@ private String call(byte[] ownerAddressByte, byte[] contractAddressByte, long va } result = ByteArray.toJsonHex(listBytes); } else { - logger.error("trigger contract failed."); String errMsg = retBuilder.getMessage().toStringUtf8(); byte[] resData = trxExtBuilder.getConstantResult(0).toByteArray(); if (resData.length > 4 && Hex.toHexString(resData).startsWith(ERROR_SELECTOR)) { @@ -478,7 +477,12 @@ private String call(byte[] ownerAddressByte, byte[] contractAddressByte, long va errMsg += ": " + msg; } - throw new JsonRpcInternalException(errMsg); + if (resData.length > 0) { + throw new JsonRpcInternalException(errMsg, ByteArray.toJsonHex(resData)); + } else { + throw new JsonRpcInternalException(errMsg); + } + } return result; @@ -639,7 +643,12 @@ public String estimateGas(CallArguments args) throws JsonRpcInvalidRequestExcept errMsg += ": " + msg; } - throw new JsonRpcInternalException(errMsg); + if (data.length > 0) { + throw new JsonRpcInternalException(errMsg, ByteArray.toJsonHex(data)); + } else { + throw new JsonRpcInternalException(errMsg); + } + } else { if (supportEstimateEnergy) { diff --git a/framework/src/main/java/org/tron/core/services/jsonrpc/filters/LogBlockQuery.java b/framework/src/main/java/org/tron/core/services/jsonrpc/filters/LogBlockQuery.java index 7665c51106f..2c6773d8489 100644 --- a/framework/src/main/java/org/tron/core/services/jsonrpc/filters/LogBlockQuery.java +++ b/framework/src/main/java/org/tron/core/services/jsonrpc/filters/LogBlockQuery.java @@ -10,7 +10,7 @@ import org.apache.commons.lang3.ArrayUtils; import org.tron.common.bloom.Bloom; import org.tron.common.crypto.Hash; -import org.tron.core.exception.JsonRpcTooManyResultException; +import org.tron.core.exception.jsonrpc.JsonRpcTooManyResultException; import org.tron.core.store.SectionBloomStore; /** diff --git a/framework/src/main/java/org/tron/core/services/jsonrpc/filters/LogFilter.java b/framework/src/main/java/org/tron/core/services/jsonrpc/filters/LogFilter.java index ce315e506d2..42bc123d4bc 100644 --- a/framework/src/main/java/org/tron/core/services/jsonrpc/filters/LogFilter.java +++ b/framework/src/main/java/org/tron/core/services/jsonrpc/filters/LogFilter.java @@ -14,7 +14,7 @@ import org.tron.common.crypto.Hash; import org.tron.common.runtime.vm.DataWord; import org.tron.core.config.args.Args; -import org.tron.core.exception.JsonRpcInvalidParamsException; +import org.tron.core.exception.jsonrpc.JsonRpcInvalidParamsException; import org.tron.core.services.jsonrpc.TronJsonRpc.FilterRequest; import org.tron.protos.Protocol.TransactionInfo.Log; diff --git a/framework/src/main/java/org/tron/core/services/jsonrpc/filters/LogFilterAndResult.java b/framework/src/main/java/org/tron/core/services/jsonrpc/filters/LogFilterAndResult.java index 3b893aec4cf..57739819d1e 100644 --- a/framework/src/main/java/org/tron/core/services/jsonrpc/filters/LogFilterAndResult.java +++ b/framework/src/main/java/org/tron/core/services/jsonrpc/filters/LogFilterAndResult.java @@ -5,7 +5,7 @@ import java.util.concurrent.LinkedBlockingQueue; import lombok.Getter; import org.tron.core.Wallet; -import org.tron.core.exception.JsonRpcInvalidParamsException; +import org.tron.core.exception.jsonrpc.JsonRpcInvalidParamsException; import org.tron.core.services.jsonrpc.TronJsonRpc.FilterRequest; import org.tron.core.services.jsonrpc.TronJsonRpc.LogFilterElement; diff --git a/framework/src/main/java/org/tron/core/services/jsonrpc/filters/LogFilterWrapper.java b/framework/src/main/java/org/tron/core/services/jsonrpc/filters/LogFilterWrapper.java index c0cd1ff12df..97a012b7f9a 100644 --- a/framework/src/main/java/org/tron/core/services/jsonrpc/filters/LogFilterWrapper.java +++ b/framework/src/main/java/org/tron/core/services/jsonrpc/filters/LogFilterWrapper.java @@ -8,7 +8,7 @@ import org.tron.common.utils.ByteArray; import org.tron.core.Wallet; import org.tron.core.config.args.Args; -import org.tron.core.exception.JsonRpcInvalidParamsException; +import org.tron.core.exception.jsonrpc.JsonRpcInvalidParamsException; import org.tron.core.services.jsonrpc.JsonRpcApiUtil; import org.tron.core.services.jsonrpc.TronJsonRpc.FilterRequest; import org.tron.protos.Protocol.Block; diff --git a/framework/src/main/java/org/tron/core/services/jsonrpc/filters/LogMatch.java b/framework/src/main/java/org/tron/core/services/jsonrpc/filters/LogMatch.java index d96aa07f9a4..2c3d3c2a52e 100644 --- a/framework/src/main/java/org/tron/core/services/jsonrpc/filters/LogMatch.java +++ b/framework/src/main/java/org/tron/core/services/jsonrpc/filters/LogMatch.java @@ -10,7 +10,7 @@ import org.tron.core.db.Manager; import org.tron.core.exception.BadItemException; import org.tron.core.exception.ItemNotFoundException; -import org.tron.core.exception.JsonRpcTooManyResultException; +import org.tron.core.exception.jsonrpc.JsonRpcTooManyResultException; import org.tron.core.services.jsonrpc.TronJsonRpc.LogFilterElement; import org.tron.protos.Protocol.TransactionInfo; import org.tron.protos.Protocol.TransactionInfo.Log; diff --git a/framework/src/main/java/org/tron/core/services/jsonrpc/types/BuildArguments.java b/framework/src/main/java/org/tron/core/services/jsonrpc/types/BuildArguments.java index 223e807e622..490219a13d9 100644 --- a/framework/src/main/java/org/tron/core/services/jsonrpc/types/BuildArguments.java +++ b/framework/src/main/java/org/tron/core/services/jsonrpc/types/BuildArguments.java @@ -14,8 +14,8 @@ import org.apache.commons.lang3.StringUtils; import org.tron.api.GrpcAPI.BytesMessage; import org.tron.core.Wallet; -import org.tron.core.exception.JsonRpcInvalidParamsException; -import org.tron.core.exception.JsonRpcInvalidRequestException; +import org.tron.core.exception.jsonrpc.JsonRpcInvalidParamsException; +import org.tron.core.exception.jsonrpc.JsonRpcInvalidRequestException; import org.tron.protos.Protocol.Transaction.Contract.ContractType; import org.tron.protos.contract.SmartContractOuterClass.SmartContract; diff --git a/framework/src/main/java/org/tron/core/services/jsonrpc/types/CallArguments.java b/framework/src/main/java/org/tron/core/services/jsonrpc/types/CallArguments.java index 1485448c4b6..70edd1ad94f 100644 --- a/framework/src/main/java/org/tron/core/services/jsonrpc/types/CallArguments.java +++ b/framework/src/main/java/org/tron/core/services/jsonrpc/types/CallArguments.java @@ -13,8 +13,8 @@ import org.apache.commons.lang3.StringUtils; import org.tron.api.GrpcAPI.BytesMessage; import org.tron.core.Wallet; -import org.tron.core.exception.JsonRpcInvalidParamsException; -import org.tron.core.exception.JsonRpcInvalidRequestException; +import org.tron.core.exception.jsonrpc.JsonRpcInvalidParamsException; +import org.tron.core.exception.jsonrpc.JsonRpcInvalidRequestException; import org.tron.protos.Protocol.Transaction.Contract.ContractType; import org.tron.protos.contract.SmartContractOuterClass.SmartContract; diff --git a/framework/src/test/java/org/tron/common/runtime/vm/Create2Test.java b/framework/src/test/java/org/tron/common/runtime/vm/Create2Test.java index f400b3215ee..6fa2801c51f 100644 --- a/framework/src/test/java/org/tron/common/runtime/vm/Create2Test.java +++ b/framework/src/test/java/org/tron/common/runtime/vm/Create2Test.java @@ -19,9 +19,9 @@ import org.tron.core.Wallet; import org.tron.core.exception.ContractExeException; import org.tron.core.exception.ContractValidateException; -import org.tron.core.exception.JsonRpcInvalidParamsException; import org.tron.core.exception.ReceiptCheckErrException; import org.tron.core.exception.VMIllegalException; +import org.tron.core.exception.jsonrpc.JsonRpcInvalidParamsException; import org.tron.core.services.NodeInfoService; import org.tron.core.services.jsonrpc.TronJsonRpcImpl; import org.tron.core.vm.config.ConfigLoader; diff --git a/framework/src/test/java/org/tron/core/CoreExceptionTest.java b/framework/src/test/java/org/tron/core/CoreExceptionTest.java index 89feaba338c..f82b0efe326 100644 --- a/framework/src/test/java/org/tron/core/CoreExceptionTest.java +++ b/framework/src/test/java/org/tron/core/CoreExceptionTest.java @@ -29,11 +29,6 @@ import org.tron.core.exception.HeaderNotFound; import org.tron.core.exception.HighFreqException; import org.tron.core.exception.ItemNotFoundException; -import org.tron.core.exception.JsonRpcInternalException; -import org.tron.core.exception.JsonRpcInvalidParamsException; -import org.tron.core.exception.JsonRpcInvalidRequestException; -import org.tron.core.exception.JsonRpcMethodNotFoundException; -import org.tron.core.exception.JsonRpcTooManyResultException; import org.tron.core.exception.NonCommonBlockException; import org.tron.core.exception.NonUniqueObjectException; import org.tron.core.exception.P2pException; @@ -56,6 +51,11 @@ import org.tron.core.exception.ValidateSignatureException; import org.tron.core.exception.ZkProofValidateException; import org.tron.core.exception.ZksnarkException; +import org.tron.core.exception.jsonrpc.JsonRpcInternalException; +import org.tron.core.exception.jsonrpc.JsonRpcInvalidParamsException; +import org.tron.core.exception.jsonrpc.JsonRpcInvalidRequestException; +import org.tron.core.exception.jsonrpc.JsonRpcMethodNotFoundException; +import org.tron.core.exception.jsonrpc.JsonRpcTooManyResultException; public class CoreExceptionTest { diff --git a/framework/src/test/java/org/tron/core/jsonrpc/JsonRpcTest.java b/framework/src/test/java/org/tron/core/jsonrpc/JsonRpcTest.java index bef0b5a1593..bd357101da3 100644 --- a/framework/src/test/java/org/tron/core/jsonrpc/JsonRpcTest.java +++ b/framework/src/test/java/org/tron/core/jsonrpc/JsonRpcTest.java @@ -18,7 +18,7 @@ import org.tron.common.utils.ByteArray; import org.tron.common.utils.ByteUtil; import org.tron.common.utils.Commons; -import org.tron.core.exception.JsonRpcInvalidParamsException; +import org.tron.core.exception.jsonrpc.JsonRpcInvalidParamsException; import org.tron.core.services.jsonrpc.JsonRpcApiUtil; import org.tron.core.services.jsonrpc.TronJsonRpc.FilterRequest; import org.tron.core.services.jsonrpc.filters.LogBlockQuery; diff --git a/framework/src/test/java/org/tron/core/jsonrpc/JsonrpcServiceTest.java b/framework/src/test/java/org/tron/core/jsonrpc/JsonrpcServiceTest.java index df0e13d6b6c..f3069faead9 100644 --- a/framework/src/test/java/org/tron/core/jsonrpc/JsonrpcServiceTest.java +++ b/framework/src/test/java/org/tron/core/jsonrpc/JsonrpcServiceTest.java @@ -35,7 +35,7 @@ import org.tron.core.capsule.TransactionCapsule; import org.tron.core.capsule.utils.BlockUtil; import org.tron.core.config.args.Args; -import org.tron.core.exception.JsonRpcInvalidParamsException; +import org.tron.core.exception.jsonrpc.JsonRpcInvalidParamsException; import org.tron.core.services.NodeInfoService; import org.tron.core.services.interfaceJsonRpcOnPBFT.JsonRpcServiceOnPBFT; import org.tron.core.services.interfaceJsonRpcOnSolidity.JsonRpcServiceOnSolidity; diff --git a/framework/src/test/java/org/tron/core/jsonrpc/LogMatchExactlyTest.java b/framework/src/test/java/org/tron/core/jsonrpc/LogMatchExactlyTest.java index 600cc52b58e..f55e3bc2cfa 100644 --- a/framework/src/test/java/org/tron/core/jsonrpc/LogMatchExactlyTest.java +++ b/framework/src/test/java/org/tron/core/jsonrpc/LogMatchExactlyTest.java @@ -9,7 +9,7 @@ import org.tron.common.runtime.vm.DataWord; import org.tron.common.runtime.vm.LogInfo; import org.tron.common.utils.ByteArray; -import org.tron.core.exception.JsonRpcInvalidParamsException; +import org.tron.core.exception.jsonrpc.JsonRpcInvalidParamsException; import org.tron.core.services.jsonrpc.TronJsonRpc.FilterRequest; import org.tron.core.services.jsonrpc.TronJsonRpc.LogFilterElement; import org.tron.core.services.jsonrpc.filters.LogFilter; diff --git a/framework/src/test/java/org/tron/core/services/jsonrpc/BuildArgumentsTest.java b/framework/src/test/java/org/tron/core/services/jsonrpc/BuildArgumentsTest.java index f9e264c515f..952e9c81467 100644 --- a/framework/src/test/java/org/tron/core/services/jsonrpc/BuildArgumentsTest.java +++ b/framework/src/test/java/org/tron/core/services/jsonrpc/BuildArgumentsTest.java @@ -8,8 +8,8 @@ import org.tron.core.Constant; import org.tron.core.Wallet; import org.tron.core.config.args.Args; -import org.tron.core.exception.JsonRpcInvalidParamsException; -import org.tron.core.exception.JsonRpcInvalidRequestException; +import org.tron.core.exception.jsonrpc.JsonRpcInvalidParamsException; +import org.tron.core.exception.jsonrpc.JsonRpcInvalidRequestException; import org.tron.core.services.jsonrpc.types.BuildArguments; import org.tron.core.services.jsonrpc.types.CallArguments; import org.tron.protos.Protocol; diff --git a/framework/src/test/java/org/tron/core/services/jsonrpc/CallArgumentsTest.java b/framework/src/test/java/org/tron/core/services/jsonrpc/CallArgumentsTest.java index 19dd76e5e07..1d7f568453b 100644 --- a/framework/src/test/java/org/tron/core/services/jsonrpc/CallArgumentsTest.java +++ b/framework/src/test/java/org/tron/core/services/jsonrpc/CallArgumentsTest.java @@ -8,8 +8,8 @@ import org.tron.core.Constant; import org.tron.core.Wallet; import org.tron.core.config.args.Args; -import org.tron.core.exception.JsonRpcInvalidParamsException; -import org.tron.core.exception.JsonRpcInvalidRequestException; +import org.tron.core.exception.jsonrpc.JsonRpcInvalidParamsException; +import org.tron.core.exception.jsonrpc.JsonRpcInvalidRequestException; import org.tron.core.services.jsonrpc.types.CallArguments; import org.tron.protos.Protocol; diff --git a/framework/src/test/java/org/tron/core/services/jsonrpc/JsonRpcErrorResolverTest.java b/framework/src/test/java/org/tron/core/services/jsonrpc/JsonRpcErrorResolverTest.java new file mode 100644 index 00000000000..d8e64308ab8 --- /dev/null +++ b/framework/src/test/java/org/tron/core/services/jsonrpc/JsonRpcErrorResolverTest.java @@ -0,0 +1,75 @@ +package org.tron.core.services.jsonrpc; + +import com.fasterxml.jackson.databind.JsonNode; +import com.googlecode.jsonrpc4j.ErrorData; +import com.googlecode.jsonrpc4j.ErrorResolver.JsonError; +import com.googlecode.jsonrpc4j.JsonRpcError; +import com.googlecode.jsonrpc4j.JsonRpcErrors; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import org.junit.Assert; +import org.junit.Test; +import org.tron.core.exception.jsonrpc.JsonRpcException; +import org.tron.core.exception.jsonrpc.JsonRpcInternalException; +import org.tron.core.exception.jsonrpc.JsonRpcInvalidParamsException; +import org.tron.core.exception.jsonrpc.JsonRpcInvalidRequestException; + +public class JsonRpcErrorResolverTest { + + private final JsonRpcErrorResolver resolver = JsonRpcErrorResolver.INSTANCE; + + @JsonRpcErrors({ + @JsonRpcError(exception = JsonRpcInvalidRequestException.class, code = -32600, data = "{}"), + @JsonRpcError(exception = JsonRpcInvalidParamsException.class, code = -32602, data = "{}"), + @JsonRpcError(exception = JsonRpcInternalException.class, code = -32000, data = "{}"), + @JsonRpcError(exception = JsonRpcException.class, code = -1) + }) + public void dummyMethod() { + } + + @Test + public void testResolveErrorWithTronException() throws Exception { + + String message = "JsonRpcInvalidRequestException"; + + JsonRpcException exception = new JsonRpcInvalidRequestException(message); + Method method = this.getClass().getMethod("dummyMethod"); + List arguments = new ArrayList<>(); + + JsonError error = resolver.resolveError(exception, method, arguments); + Assert.assertNotNull(error); + Assert.assertEquals(-32600, error.code); + Assert.assertEquals(message, error.message); + Assert.assertEquals("{}", error.data); + + message = "JsonRpcInternalException"; + String data = "JsonRpcInternalException data"; + exception = new JsonRpcInternalException(message, data); + error = resolver.resolveError(exception, method, arguments); + + Assert.assertNotNull(error); + Assert.assertEquals(-32000, error.code); + Assert.assertEquals(message, error.message); + Assert.assertEquals(data, error.data); + + exception = new JsonRpcInternalException(message, null); + error = resolver.resolveError(exception, method, arguments); + + Assert.assertNotNull(error); + Assert.assertEquals(-32000, error.code); + Assert.assertEquals(message, error.message); + Assert.assertEquals("{}", error.data); + + message = "JsonRpcException"; + exception = new JsonRpcException(message, null); + error = resolver.resolveError(exception, method, arguments); + + Assert.assertNotNull(error); + Assert.assertEquals(-1, error.code); + Assert.assertEquals(message, error.message); + Assert.assertTrue(error.data instanceof ErrorData); + + } + +} \ No newline at end of file From 2dcdb4537a243c5f48136bb9e80591e5edeaafd3 Mon Sep 17 00:00:00 2001 From: 0xbigapple Date: Fri, 15 Aug 2025 10:03:46 +0800 Subject: [PATCH 28/59] feat(jsonrpc): optimize event log query (#6370) * feat(api): optimize partialMatch * feat(api): remove skipGroups in partialMatch * feat(api): fix bug * feat(api): add test * feat(api): add unit test * feat(api): add inline comments * feat(api): remove unnecessary code --- .../jsonrpc/filters/LogBlockQuery.java | 82 ++++++++++---- .../tron/core/jsonrpc/LogBlockQueryTest.java | 100 ++++++++++++++++++ 2 files changed, 159 insertions(+), 23 deletions(-) create mode 100644 framework/src/test/java/org/tron/core/jsonrpc/LogBlockQueryTest.java diff --git a/framework/src/main/java/org/tron/core/services/jsonrpc/filters/LogBlockQuery.java b/framework/src/main/java/org/tron/core/services/jsonrpc/filters/LogBlockQuery.java index 2c6773d8489..2daf2ca3271 100644 --- a/framework/src/main/java/org/tron/core/services/jsonrpc/filters/LogBlockQuery.java +++ b/framework/src/main/java/org/tron/core/services/jsonrpc/filters/LogBlockQuery.java @@ -2,7 +2,11 @@ import java.util.ArrayList; import java.util.BitSet; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; @@ -121,44 +125,76 @@ private BitSet subMatch(int[][] bitIndexes) throws ExecutionException, Interrupt } /** - * every section has a compound query of sectionBloomStore, works parallel - * "and" condition in second dimension of query, "or" condition in first dimension - * return a BitSet whose capacity is blockPerSection + * Match blocks using optimized bloom filter operations. This method reduces database queries + * and BitSet operations by handling duplicate bit indexes and skipping invalid groups. + * + * @param bitIndexes A 2D array where: + * - First dimension represents different topic/address (OR) + * - Second dimension contains bit indexes within each topic/address (AND) + * Example: [[1,2,3], [4,5,6]] means (1 AND 2 AND 3) OR (4 AND 5 AND 6) + * @param section The section number in the bloom filter store to query + * @return A BitSet representing the matching blocks in this section + * @throws ExecutionException If there's an error in concurrent execution + * @throws InterruptedException If the concurrent execution is interrupted */ private BitSet partialMatch(final int[][] bitIndexes, int section) throws ExecutionException, InterruptedException { - List>> bitSetList = new ArrayList<>(); - + // 1. Collect all unique bitIndexes + Set uniqueBitIndexes = new HashSet<>(); for (int[] index : bitIndexes) { - List> futureList = new ArrayList<>(); - for (final int bitIndex : index) { //must be 3 - Future bitSetFuture = - sectionExecutor.submit(() -> sectionBloomStore.get(section, bitIndex)); - futureList.add(bitSetFuture); + for (int bitIndex : index) { //normally 3, but could be less due to hash collisions + uniqueBitIndexes.add(bitIndex); + } + } + + // 2. Submit concurrent requests for all unique bitIndexes + Map> bitIndexResults = new HashMap<>(); + for (int bitIndex : uniqueBitIndexes) { + Future future + = sectionExecutor.submit(() -> sectionBloomStore.get(section, bitIndex)); + bitIndexResults.put(bitIndex, future); + } + + // 3. Wait for all results and cache them + Map resultCache = new HashMap<>(); + for (Map.Entry> entry : bitIndexResults.entrySet()) { + BitSet result = entry.getValue().get(); + if (result != null) { + resultCache.put(entry.getKey(), result); } - bitSetList.add(futureList); } - BitSet bitSet = new BitSet(SectionBloomStore.BLOCK_PER_SECTION); - for (List> futureList : bitSetList) { - // initial a BitSet with all 1 - BitSet subBitSet = new BitSet(SectionBloomStore.BLOCK_PER_SECTION); - subBitSet.set(0, SectionBloomStore.BLOCK_PER_SECTION); + // 4. Process valid groups with reused BitSet objects + BitSet finalResult = new BitSet(SectionBloomStore.BLOCK_PER_SECTION); + BitSet tempBitSet = new BitSet(SectionBloomStore.BLOCK_PER_SECTION); + + for (int[] index : bitIndexes) { + + // init tempBitSet with all 1 + tempBitSet.set(0, SectionBloomStore.BLOCK_PER_SECTION); + // and condition in second dimension - for (Future future : futureList) { - BitSet one = future.get(); - if (one == null) { //match nothing - subBitSet.clear(); + for (int bitIndex : index) { + BitSet cached = resultCache.get(bitIndex); + if (cached == null) { //match nothing + tempBitSet.clear(); break; } // "and" condition in second dimension - subBitSet.and(one); + tempBitSet.and(cached); + if (tempBitSet.isEmpty()) { + break; + } } + // "or" condition in first dimension - bitSet.or(subBitSet); + if (!tempBitSet.isEmpty()) { + finalResult.or(tempBitSet); + } } - return bitSet; + + return finalResult; } /** diff --git a/framework/src/test/java/org/tron/core/jsonrpc/LogBlockQueryTest.java b/framework/src/test/java/org/tron/core/jsonrpc/LogBlockQueryTest.java new file mode 100644 index 00000000000..deae8babb32 --- /dev/null +++ b/framework/src/test/java/org/tron/core/jsonrpc/LogBlockQueryTest.java @@ -0,0 +1,100 @@ +package org.tron.core.jsonrpc; + +import java.lang.reflect.Method; +import java.util.BitSet; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import javax.annotation.Resource; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.tron.common.BaseTest; +import org.tron.core.Constant; +import org.tron.core.config.args.Args; +import org.tron.core.services.jsonrpc.TronJsonRpc.FilterRequest; +import org.tron.core.services.jsonrpc.filters.LogBlockQuery; +import org.tron.core.services.jsonrpc.filters.LogFilterWrapper; +import org.tron.core.store.SectionBloomStore; + +public class LogBlockQueryTest extends BaseTest { + + @Resource + SectionBloomStore sectionBloomStore; + private ExecutorService sectionExecutor; + private Method partialMatchMethod; + private static final long CURRENT_MAX_BLOCK_NUM = 50000L; + + static { + Args.setParam(new String[] {"--output-directory", dbPath()}, Constant.TEST_CONF); + } + + @Before + public void setup() throws Exception { + sectionExecutor = Executors.newFixedThreadPool(5); + + // Get private method through reflection + partialMatchMethod = LogBlockQuery.class.getDeclaredMethod("partialMatch", + int[][].class, int.class); + partialMatchMethod.setAccessible(true); + + BitSet bitSet = new BitSet(SectionBloomStore.BLOCK_PER_SECTION); + bitSet.set(0, SectionBloomStore.BLOCK_PER_SECTION); + sectionBloomStore.put(0, 1, bitSet); + sectionBloomStore.put(0, 2, bitSet); + sectionBloomStore.put(0, 3, bitSet); + BitSet bitSet2 = new BitSet(SectionBloomStore.BLOCK_PER_SECTION); + bitSet2.set(0); + sectionBloomStore.put(1, 1, bitSet2); + sectionBloomStore.put(1, 2, bitSet2); + sectionBloomStore.put(1, 3, bitSet2); + } + + @Test + public void testPartialMatch() throws Exception { + // Create a basic LogFilterWrapper + LogFilterWrapper logFilterWrapper = new LogFilterWrapper( + new FilterRequest("0x0", "0x1", null, null, null), + CURRENT_MAX_BLOCK_NUM, null, false); + + LogBlockQuery logBlockQuery = new LogBlockQuery(logFilterWrapper, sectionBloomStore, + CURRENT_MAX_BLOCK_NUM, sectionExecutor); + + int section = 0; + + // Create a hit condition + int[][] bitIndexes = new int[][] { + {1, 2, 3}, // topic0 + {4, 5, 6} // topic1 + }; + + // topic0 hit section 0 + BitSet result = (BitSet) partialMatchMethod.invoke(logBlockQuery, bitIndexes, section); + Assert.assertNotNull(result); + Assert.assertEquals(SectionBloomStore.BLOCK_PER_SECTION, result.cardinality()); + + // topic0 hit section 1 + result = (BitSet) partialMatchMethod.invoke(logBlockQuery, bitIndexes, 1); + Assert.assertNotNull(result); + Assert.assertEquals(1, result.cardinality()); + + // not exist section 2 + result = (BitSet) partialMatchMethod.invoke(logBlockQuery, bitIndexes, 2); + Assert.assertNotNull(result); + Assert.assertTrue(result.isEmpty()); + + //not hit + bitIndexes = new int[][] { + {1, 2, 4}, // topic0 + {3, 5, 6} // topic1 + }; + result = (BitSet) partialMatchMethod.invoke(logBlockQuery, bitIndexes, section); + Assert.assertNotNull(result); + Assert.assertTrue(result.isEmpty()); + + // null condition + bitIndexes = new int[0][]; + result = (BitSet) partialMatchMethod.invoke(logBlockQuery, bitIndexes, section); + Assert.assertNotNull(result); + Assert.assertTrue(result.isEmpty()); + } +} \ No newline at end of file From 456b4eb73609a9c39d9050e0f020cfcd61583a86 Mon Sep 17 00:00:00 2001 From: 317787106 <317787106@qq.com> Date: Mon, 18 Aug 2025 16:22:55 +0800 Subject: [PATCH 29/59] fix(net): optimize disconnect reason for light node (#6375) * fix the bug of handshake with lightnode --- .../service/handshake/HandshakeService.java | 18 ++-- .../org/tron/core/jsonrpc/ApiUtilTest.java | 1 - .../test/java/org/tron/core/net/BaseNet.java | 40 --------- .../net/services/HandShakeServiceTest.java | 19 ++++- .../core/net/services/RelayServiceTest.java | 85 +++++++++++++++---- .../services/http/GetRewardServletTest.java | 8 +- framework/src/test/resources/config-test.conf | 2 +- 7 files changed, 101 insertions(+), 72 deletions(-) diff --git a/framework/src/main/java/org/tron/core/net/service/handshake/HandshakeService.java b/framework/src/main/java/org/tron/core/net/service/handshake/HandshakeService.java index 098175c4109..070a9f56406 100644 --- a/framework/src/main/java/org/tron/core/net/service/handshake/HandshakeService.java +++ b/framework/src/main/java/org/tron/core/net/service/handshake/HandshakeService.java @@ -6,6 +6,7 @@ import org.springframework.stereotype.Component; import org.tron.common.utils.ByteArray; import org.tron.core.ChainBaseManager; +import org.tron.core.ChainBaseManager.NodeType; import org.tron.core.config.args.Args; import org.tron.core.net.TronNetService; import org.tron.core.net.message.handshake.HelloMessage; @@ -96,12 +97,17 @@ public void processHelloMessage(PeerConnection peer, HelloMessage msg) { } if (chainBaseManager.getSolidBlockId().getNum() >= msg.getSolidBlockId().getNum() - && !chainBaseManager.containBlockInMainChain(msg.getSolidBlockId())) { - logger.info("Peer {} different solid block, peer->{}, me->{}", - peer.getInetSocketAddress(), - msg.getSolidBlockId().getString(), - chainBaseManager.getSolidBlockId().getString()); - peer.disconnect(ReasonCode.FORKED); + && !chainBaseManager.containBlockInMainChain(msg.getSolidBlockId())) { + if (chainBaseManager.getLowestBlockNum() <= msg.getSolidBlockId().getNum()) { + logger.info("Peer {} different solid block, fork with me, peer->{}, me->{}", + peer.getInetSocketAddress(), + msg.getSolidBlockId().getString(), + chainBaseManager.getSolidBlockId().getString()); + peer.disconnect(ReasonCode.FORKED); + } else { + logger.info("Peer {} solid block is below than my lowest", peer.getInetSocketAddress()); + peer.disconnect(ReasonCode.LIGHT_NODE_SYNC_FAIL); + } return; } diff --git a/framework/src/test/java/org/tron/core/jsonrpc/ApiUtilTest.java b/framework/src/test/java/org/tron/core/jsonrpc/ApiUtilTest.java index 2c9afbac99b..f62d47d5367 100644 --- a/framework/src/test/java/org/tron/core/jsonrpc/ApiUtilTest.java +++ b/framework/src/test/java/org/tron/core/jsonrpc/ApiUtilTest.java @@ -19,7 +19,6 @@ public class ApiUtilTest { - @BeforeClass public static void init() { Args.setParam(new String[]{}, "config-localtest.conf"); diff --git a/framework/src/test/java/org/tron/core/net/BaseNet.java b/framework/src/test/java/org/tron/core/net/BaseNet.java index fc04e210acd..bdaab1b4301 100644 --- a/framework/src/test/java/org/tron/core/net/BaseNet.java +++ b/framework/src/test/java/org/tron/core/net/BaseNet.java @@ -1,25 +1,10 @@ package org.tron.core.net; -import io.netty.bootstrap.Bootstrap; -import io.netty.channel.Channel; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelOption; -import io.netty.channel.DefaultMessageSizeEstimator; -import io.netty.channel.FixedRecvByteBufAllocator; -import io.netty.channel.nio.NioEventLoopGroup; -import io.netty.channel.socket.nio.NioSocketChannel; -import io.netty.handler.codec.ByteToMessageDecoder; -import io.netty.handler.codec.protobuf.ProtobufVarint32FrameDecoder; -import io.netty.handler.codec.protobuf.ProtobufVarint32LengthFieldPrepender; -import io.netty.handler.timeout.ReadTimeoutHandler; -import io.netty.handler.timeout.WriteTimeoutHandler; -import java.io.File; import java.io.IOException; import java.util.Collection; import java.util.Objects; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; import lombok.extern.slf4j.Slf4j; import org.junit.AfterClass; import org.junit.Assert; @@ -30,7 +15,6 @@ import org.tron.common.application.ApplicationFactory; import org.tron.common.application.TronApplicationContext; import org.tron.common.parameter.CommonParameter; -import org.tron.common.utils.FileUtil; import org.tron.common.utils.PublicMethod; import org.tron.common.utils.ReflectUtils; import org.tron.core.Constant; @@ -54,30 +38,6 @@ public class BaseNet { private static ExecutorService executorService = Executors.newFixedThreadPool(1); - public static Channel connect(ByteToMessageDecoder decoder) throws InterruptedException { - NioEventLoopGroup group = new NioEventLoopGroup(1); - Bootstrap b = new Bootstrap(); - b.group(group).channel(NioSocketChannel.class) - .handler(new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) throws Exception { - ch.config().setRecvByteBufAllocator(new FixedRecvByteBufAllocator(256 * 1024)); - ch.config().setOption(ChannelOption.SO_RCVBUF, 256 * 1024); - ch.config().setOption(ChannelOption.SO_BACKLOG, 1024); - ch.pipeline() - .addLast("readTimeoutHandler", new ReadTimeoutHandler(600, TimeUnit.SECONDS)) - .addLast("writeTimeoutHandler", new WriteTimeoutHandler(600, TimeUnit.SECONDS)); - ch.pipeline().addLast("protoPender", new ProtobufVarint32LengthFieldPrepender()); - ch.pipeline().addLast("lengthDecode", new ProtobufVarint32FrameDecoder()); - ch.pipeline().addLast("handshakeHandler", decoder); - ch.closeFuture(); - } - }).option(ChannelOption.SO_KEEPALIVE, true) - .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 60000) - .option(ChannelOption.MESSAGE_SIZE_ESTIMATOR, DefaultMessageSizeEstimator.DEFAULT); - return b.connect(Constant.LOCAL_HOST, port).sync().channel(); - } - @BeforeClass public static void init() throws Exception { executorService.execute(() -> { diff --git a/framework/src/test/java/org/tron/core/net/services/HandShakeServiceTest.java b/framework/src/test/java/org/tron/core/net/services/HandShakeServiceTest.java index 8e9a3fa7166..54ee84cee6c 100644 --- a/framework/src/test/java/org/tron/core/net/services/HandShakeServiceTest.java +++ b/framework/src/test/java/org/tron/core/net/services/HandShakeServiceTest.java @@ -101,6 +101,7 @@ public void testOkHelloMessage() Node node = new Node(NetUtil.getNodeId(), a1.getAddress().getHostAddress(), null, a1.getPort()); HelloMessage helloMessage = new HelloMessage(node, System.currentTimeMillis(), ChainBaseManager.getChainBaseManager()); + Assert.assertNotNull(helloMessage.toString()); Assert.assertEquals(Version.getVersion(), new String(helloMessage.getHelloMessage().getCodeVersion().toByteArray())); @@ -228,7 +229,7 @@ public void testLowAndGenesisBlockNum() throws NoSuchMethodException { Node node2 = new Node(NetUtil.getNodeId(), a1.getAddress().getHostAddress(), null, 10002); - //lowestBlockNum > headBlockNum + //peer's lowestBlockNum > my headBlockNum => peer is light, LIGHT_NODE_SYNC_FAIL Protocol.HelloMessage.Builder builder = getHelloMessageBuilder(node2, System.currentTimeMillis(), ChainBaseManager.getChainBaseManager()); @@ -240,7 +241,7 @@ public void testLowAndGenesisBlockNum() throws NoSuchMethodException { Assert.fail(); } - //genesisBlock is not equal + //genesisBlock is not equal => INCOMPATIBLE_CHAIN builder = getHelloMessageBuilder(node2, System.currentTimeMillis(), ChainBaseManager.getChainBaseManager()); BlockCapsule.BlockId gid = ChainBaseManager.getChainBaseManager().getGenesisBlockId(); @@ -256,9 +257,11 @@ public void testLowAndGenesisBlockNum() throws NoSuchMethodException { Assert.fail(); } - //solidityBlock <= us, but not contained + // peer's solidityBlock <= my solidityBlock, but not contained + // and my lowestBlockNum <= peer's solidityBlock => FORKED builder = getHelloMessageBuilder(node2, System.currentTimeMillis(), ChainBaseManager.getChainBaseManager()); + BlockCapsule.BlockId sid = ChainBaseManager.getChainBaseManager().getSolidBlockId(); Random gen = new Random(); @@ -276,6 +279,16 @@ public void testLowAndGenesisBlockNum() throws NoSuchMethodException { } catch (Exception e) { Assert.fail(); } + + // peer's solidityBlock <= my solidityBlock, but not contained + // and my lowestBlockNum > peer's solidityBlock => i am light, LIGHT_NODE_SYNC_FAIL + ChainBaseManager.getChainBaseManager().setLowestBlockNum(2); + try { + HelloMessage helloMessage = new HelloMessage(builder.build().toByteArray()); + method.invoke(p2pEventHandler, peer, helloMessage.getSendBytes()); + } catch (Exception e) { + Assert.fail(); + } } @Test diff --git a/framework/src/test/java/org/tron/core/net/services/RelayServiceTest.java b/framework/src/test/java/org/tron/core/net/services/RelayServiceTest.java index 5e22e538e80..2ed09019fc9 100644 --- a/framework/src/test/java/org/tron/core/net/services/RelayServiceTest.java +++ b/framework/src/test/java/org/tron/core/net/services/RelayServiceTest.java @@ -1,8 +1,9 @@ package org.tron.core.net.services; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; -import com.google.common.collect.Lists; import com.google.protobuf.ByteString; import java.lang.reflect.Field; import java.lang.reflect.Method; @@ -12,7 +13,6 @@ import java.util.List; import java.util.Set; import javax.annotation.Resource; - import lombok.extern.slf4j.Slf4j; import org.bouncycastle.util.encoders.Hex; import org.junit.Assert; @@ -21,19 +21,27 @@ import org.mockito.Mockito; import org.springframework.context.ApplicationContext; import org.tron.common.BaseTest; +import org.tron.common.crypto.SignInterface; +import org.tron.common.crypto.SignUtils; +import org.tron.common.parameter.CommonParameter; +import org.tron.common.utils.ByteArray; import org.tron.common.utils.ReflectUtils; +import org.tron.common.utils.Sha256Hash; import org.tron.core.ChainBaseManager; import org.tron.core.Constant; import org.tron.core.capsule.BlockCapsule; import org.tron.core.capsule.WitnessCapsule; import org.tron.core.config.args.Args; import org.tron.core.net.P2pEventHandlerImpl; +import org.tron.core.net.TronNetDelegate; +import org.tron.core.net.TronNetService; import org.tron.core.net.message.adv.BlockMessage; import org.tron.core.net.message.handshake.HelloMessage; import org.tron.core.net.peer.Item; import org.tron.core.net.peer.PeerConnection; import org.tron.core.net.peer.PeerManager; import org.tron.core.net.service.relay.RelayService; +import org.tron.p2p.P2pConfig; import org.tron.p2p.connection.Channel; import org.tron.p2p.discover.Node; import org.tron.p2p.utils.NetUtil; @@ -44,11 +52,13 @@ public class RelayServiceTest extends BaseTest { @Resource private RelayService service; - @Resource - private PeerConnection peer; + @Resource private P2pEventHandlerImpl p2pEventHandler; + @Resource + private TronNetService tronNetService; + /** * init context. */ @@ -67,8 +77,12 @@ public void test() throws Exception { } private void initWitness() { - byte[] key = Hex.decode("A04711BF7AFBDF44557DEFBDF4C4E7AA6138C6331F"); + // key: 0154435f065a57fec6af1e12eaa2fa600030639448d7809f4c65bdcf8baed7e5 + // Hex: A08A8D690BF36806C36A7DAE3AF796643C1AA9CC01 + // Base58: 27bi7CD8d94AgXY3XFS9A9vx78Si5MqrECz + byte[] key = Hex.decode("A08A8D690BF36806C36A7DAE3AF796643C1AA9CC01");//exist already WitnessCapsule witnessCapsule = chainBaseManager.getWitnessStore().get(key); + System.out.println(witnessCapsule.getInstance()); witnessCapsule.setVoteCount(1000); chainBaseManager.getWitnessStore().put(key, witnessCapsule); List list = new ArrayList<>(); @@ -83,7 +97,7 @@ public void testGetNextWitnesses() throws Exception { "getNextWitnesses", ByteString.class, Integer.class); method.setAccessible(true); Set s1 = (Set) method.invoke( - service, getFromHexString("A04711BF7AFBDF44557DEFBDF4C4E7AA6138C6331F"), 3); + service, getFromHexString("A08A8D690BF36806C36A7DAE3AF796643C1AA9CC01"), 3); Assert.assertEquals(3, s1.size()); assertContains(s1, "A0299F3DB80A24B20A254B89CE639D59132F157F13"); assertContains(s1, "A0807337F180B62A77576377C1D0C9C24DF5C0DD62"); @@ -93,35 +107,51 @@ public void testGetNextWitnesses() throws Exception { service, getFromHexString("A0FAB5FBF6AFB681E4E37E9D33BDDB7E923D6132E5"), 3); Assert.assertEquals(3, s2.size()); assertContains(s2, "A014EEBE4D30A6ACB505C8B00B218BDC4733433C68"); - assertContains(s2, "A04711BF7AFBDF44557DEFBDF4C4E7AA6138C6331F"); + assertContains(s2, "A08A8D690BF36806C36A7DAE3AF796643C1AA9CC01"); assertContains(s2, "A0299F3DB80A24B20A254B89CE639D59132F157F13"); Set s3 = (Set) method.invoke( - service, getFromHexString("A04711BF7AFBDF44557DEFBDF4C4E7AA6138C6331F"), 1); + service, getFromHexString("A08A8D690BF36806C36A7DAE3AF796643C1AA9CC01"), 1); Assert.assertEquals(1, s3.size()); assertContains(s3, "A0299F3DB80A24B20A254B89CE639D59132F157F13"); } private void testBroadcast() { try { + PeerConnection peer = new PeerConnection(); + InetSocketAddress a1 = new InetSocketAddress("127.0.0.2", 10001); + Channel c1 = mock(Channel.class); + Mockito.when(c1.getInetSocketAddress()).thenReturn(a1); + Mockito.when(c1.getInetAddress()).thenReturn(a1.getAddress()); + doNothing().when(c1).send((byte[]) any()); + + peer.setChannel(c1); peer.setAddress(getFromHexString("A0299F3DB80A24B20A254B89CE639D59132F157F13")); peer.setNeedSyncFromPeer(false); peer.setNeedSyncFromUs(false); - List peers = Lists.newArrayList(); + List peers = new ArrayList<>(); peers.add(peer); - ReflectUtils.setFieldValue(p2pEventHandler, "activePeers", peers); + + TronNetDelegate tronNetDelegate = Mockito.mock(TronNetDelegate.class); + Mockito.doReturn(peers).when(tronNetDelegate).getActivePeer(); + + Field field = service.getClass().getDeclaredField("tronNetDelegate"); + field.setAccessible(true); + field.set(service, tronNetDelegate); + BlockCapsule blockCapsule = new BlockCapsule(chainBaseManager.getHeadBlockNum() + 1, chainBaseManager.getHeadBlockId(), - 0, getFromHexString("A04711BF7AFBDF44557DEFBDF4C4E7AA6138C6331F")); + 0, getFromHexString("A08A8D690BF36806C36A7DAE3AF796643C1AA9CC01")); BlockMessage msg = new BlockMessage(blockCapsule); service.broadcast(msg); Item item = new Item(blockCapsule.getBlockId(), Protocol.Inventory.InventoryType.BLOCK); Assert.assertEquals(1, peer.getAdvInvSpread().size()); Assert.assertNotNull(peer.getAdvInvSpread().getIfPresent(item)); peer.getChannel().close(); - } catch (NullPointerException e) { - System.out.println(e); + } catch (Exception e) { + logger.info("", e); + assert false; } } @@ -135,20 +165,32 @@ private ByteString getFromHexString(String s) { } private void testCheckHelloMessage() { - ByteString address = getFromHexString("A04711BF7AFBDF44557DEFBDF4C4E7AA6138C6331F"); + String key = "0154435f065a57fec6af1e12eaa2fa600030639448d7809f4c65bdcf8baed7e5"; + ByteString address = getFromHexString("A08A8D690BF36806C36A7DAE3AF796643C1AA9CC01"); InetSocketAddress a1 = new InetSocketAddress("127.0.0.1", 10001); Node node = new Node(NetUtil.getNodeId(), a1.getAddress().getHostAddress(), null, a1.getPort()); + + SignInterface cryptoEngine = SignUtils.fromPrivate(ByteArray.fromHexString(key), + Args.getInstance().isECKeyCryptoEngine()); HelloMessage helloMessage = new HelloMessage(node, System.currentTimeMillis(), ChainBaseManager.getChainBaseManager()); + ByteString sig = ByteString.copyFrom(cryptoEngine.Base64toBytes(cryptoEngine + .signHash(Sha256Hash.of(CommonParameter.getInstance() + .isECKeyCryptoEngine(), ByteArray.fromLong(helloMessage + .getTimestamp())).getBytes()))); helloMessage.setHelloMessage(helloMessage.getHelloMessage().toBuilder() - .setAddress(address).build()); + .setAddress(address) + .setSignature(sig) + .build()); + Channel c1 = mock(Channel.class); Mockito.when(c1.getInetSocketAddress()).thenReturn(a1); Mockito.when(c1.getInetAddress()).thenReturn(a1.getAddress()); Channel c2 = mock(Channel.class); Mockito.when(c2.getInetSocketAddress()).thenReturn(a1); Mockito.when(c2.getInetAddress()).thenReturn(a1.getAddress()); + Args.getInstance().fastForward = true; ApplicationContext ctx = (ApplicationContext) ReflectUtils.getFieldObject(p2pEventHandler, "ctx"); @@ -158,14 +200,23 @@ private void testCheckHelloMessage() { PeerConnection peer2 = PeerManager.add(ctx, c2); assert peer2 != null; peer2.setAddress(address); + + ReflectUtils.setFieldValue(tronNetService, "p2pConfig", new P2pConfig()); + try { Field field = service.getClass().getDeclaredField("witnessScheduleStore"); field.setAccessible(true); field.set(service, chainBaseManager.getWitnessScheduleStore()); + + Field field2 = service.getClass().getDeclaredField("manager"); + field2.setAccessible(true); + field2.set(service, dbManager); + boolean res = service.checkHelloMessage(helloMessage, c1); - Assert.assertFalse(res); + Assert.assertTrue(res); } catch (Exception e) { - logger.info("{}", e.getMessage()); + logger.info("", e); + assert false; } } } \ No newline at end of file diff --git a/framework/src/test/java/org/tron/core/services/http/GetRewardServletTest.java b/framework/src/test/java/org/tron/core/services/http/GetRewardServletTest.java index 404e154a4c3..bd367fc3700 100644 --- a/framework/src/test/java/org/tron/core/services/http/GetRewardServletTest.java +++ b/framework/src/test/java/org/tron/core/services/http/GetRewardServletTest.java @@ -58,7 +58,7 @@ public MockHttpServletRequest createRequest(String contentType) { @Before public void init() { manager.getDynamicPropertiesStore().saveChangeDelegation(1); - byte[] sr = decodeFromBase58Check("27VZHn9PFZwNh7o2EporxmLkpe157iWZVkh"); + byte[] sr = decodeFromBase58Check("27bi7CD8d94AgXY3XFS9A9vx78Si5MqrECz"); delegationStore.setBrokerage(0, sr, 10); delegationStore.setWitnessVote(0, sr, 100000000); } @@ -66,7 +66,7 @@ public void init() { @Test public void getRewardValueByJsonTest() { int expect = 138181; - String jsonParam = "{\"address\": \"27VZHn9PFZwNh7o2EporxmLkpe157iWZVkh\"}"; + String jsonParam = "{\"address\": \"27bi7CD8d94AgXY3XFS9A9vx78Si5MqrECz\"}"; MockHttpServletRequest request = createRequest("application/json"); MockHttpServletResponse response = new MockHttpServletResponse(); request.setContent(jsonParam.getBytes()); @@ -84,7 +84,7 @@ public void getRewardValueByJsonTest() { @Test public void getRewardByJsonUTF8Test() { int expect = 138181; - String jsonParam = "{\"address\": \"27VZHn9PFZwNh7o2EporxmLkpe157iWZVkh\"}"; + String jsonParam = "{\"address\": \"27bi7CD8d94AgXY3XFS9A9vx78Si5MqrECz\"}"; MockHttpServletRequest request = createRequest("application/json; charset=utf-8"); MockHttpServletResponse response = new MockHttpServletResponse(); request.setContent(jsonParam.getBytes()); @@ -105,7 +105,7 @@ public void getRewardValueTest() { MockHttpServletRequest request = createRequest("application/x-www-form-urlencoded"); MockHttpServletResponse response = new MockHttpServletResponse(); mortgageService.payStandbyWitness(); - request.addParameter("address", "27VZHn9PFZwNh7o2EporxmLkpe157iWZVkh"); + request.addParameter("address", "27bi7CD8d94AgXY3XFS9A9vx78Si5MqrECz"); getRewardServlet.doPost(request, response); try { String contentAsString = response.getContentAsString(); diff --git a/framework/src/test/resources/config-test.conf b/framework/src/test/resources/config-test.conf index eaa6659a8c4..f7884eec831 100644 --- a/framework/src/test/resources/config-test.conf +++ b/framework/src/test/resources/config-test.conf @@ -327,7 +327,7 @@ genesis.block = { voteCount = 96 }, { - address: 27VZHn9PFZwNh7o2EporxmLkpe157iWZVkh + address: 27bi7CD8d94AgXY3XFS9A9vx78Si5MqrECz url = "http://AlphaLyrae.org", voteCount = 95 } From 00d6b77f1835a9d1453f08b35d709f012e582ad6 Mon Sep 17 00:00:00 2001 From: 0xbigapple Date: Mon, 18 Aug 2025 20:59:56 +0800 Subject: [PATCH 30/59] feat(jsonrpc): implement eth_getBlockReceipts (#6379) --- .../core/services/jsonrpc/TronJsonRpc.java | 8 + .../services/jsonrpc/TronJsonRpcImpl.java | 123 ++++++++++- .../jsonrpc/types/TransactionReceipt.java | 206 ++++++++---------- .../tron/core/jsonrpc/JsonrpcServiceTest.java | 120 ++++++++++ .../jsonrpc/TransactionReceiptTest.java | 69 ++++-- 5 files changed, 390 insertions(+), 136 deletions(-) diff --git a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpc.java b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpc.java index c078cecdb66..5e6fc771c80 100644 --- a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpc.java +++ b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpc.java @@ -146,6 +146,14 @@ TransactionResult getTransactionByBlockNumberAndIndex(String blockNumOrTag, Stri }) TransactionReceipt getTransactionReceipt(String txid) throws JsonRpcInvalidParamsException; + @JsonRpcMethod("eth_getBlockReceipts") + @JsonRpcErrors({ + @JsonRpcError(exception = JsonRpcInvalidParamsException.class, code = -32602, data = "{}"), + @JsonRpcError(exception = JsonRpcInternalException.class, code = -32000, data = "{}") + }) + List getBlockReceipts(String blockNumOrTag) + throws JsonRpcInvalidParamsException, JsonRpcInternalException; + @JsonRpcMethod("eth_call") @JsonRpcErrors({ @JsonRpcError(exception = JsonRpcInvalidRequestException.class, code = -32600, data = "{}"), diff --git a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java index 40c5f7713ae..f681a94fe9f 100644 --- a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java +++ b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java @@ -15,6 +15,7 @@ import com.google.protobuf.GeneratedMessageV3; import java.io.Closeable; import java.io.IOException; +import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; @@ -37,6 +38,7 @@ import org.tron.api.GrpcAPI.Return; import org.tron.api.GrpcAPI.Return.response_code; import org.tron.api.GrpcAPI.TransactionExtention; +import org.tron.api.GrpcAPI.TransactionInfoList; import org.tron.common.crypto.Hash; import org.tron.common.es.ExecutorServiceManager; import org.tron.common.logsfilter.ContractEventParser; @@ -75,12 +77,14 @@ import org.tron.core.services.jsonrpc.types.BuildArguments; import org.tron.core.services.jsonrpc.types.CallArguments; import org.tron.core.services.jsonrpc.types.TransactionReceipt; +import org.tron.core.services.jsonrpc.types.TransactionReceipt.TransactionContext; import org.tron.core.services.jsonrpc.types.TransactionResult; import org.tron.core.store.StorageRowStore; import org.tron.core.vm.program.Storage; import org.tron.program.Version; import org.tron.protos.Protocol.Account; import org.tron.protos.Protocol.Block; +import org.tron.protos.Protocol.ResourceReceipt; import org.tron.protos.Protocol.Transaction; import org.tron.protos.Protocol.Transaction.Contract.ContractType; import org.tron.protos.Protocol.Transaction.Result.code; @@ -93,6 +97,7 @@ import org.tron.protos.contract.SmartContractOuterClass.SmartContractDataWrapper; import org.tron.protos.contract.SmartContractOuterClass.TriggerSmartContract; + @Slf4j(topic = "API") @Component public class TronJsonRpcImpl implements TronJsonRpc, Closeable { @@ -767,6 +772,13 @@ public TransactionResult getTransactionByBlockNumberAndIndex(String blockNumOrTa return getTransactionByBlockAndIndex(block, index); } + /** + * Get a transaction receipt by transaction hash + * + * @param txId the transaction hash in hex format (with or without 0x prefix) + * @return TransactionReceipt object for the specified transaction, or null if not found + * @throws JsonRpcInvalidParamsException if the transaction hash format is invalid + */ @Override public TransactionReceipt getTransactionReceipt(String txId) throws JsonRpcInvalidParamsException { @@ -781,7 +793,116 @@ public TransactionReceipt getTransactionReceipt(String txId) return null; } - return new TransactionReceipt(block, transactionInfo, wallet); + BlockCapsule blockCapsule = new BlockCapsule(block); + long blockNum = blockCapsule.getNum(); + TransactionInfoList transactionInfoList = wallet.getTransactionInfoByBlockNum(blockNum); + long energyFee = wallet.getEnergyFee(blockCapsule.getTimeStamp()); + + // Find transaction context + TransactionReceipt.TransactionContext context + = findTransactionContext(transactionInfoList, + transactionInfo.getId()); + + if (context == null) { + return null; // Transaction not found in block + } + + return new TransactionReceipt(blockCapsule, transactionInfo, context, energyFee); + } + + /** + * Finds transaction context for a specific transaction ID within the block + * Calculates cumulative gas and log count up to the target transaction + * @param infoList the transactionInfo list for the block + * @param txId the transaction ID + * @return TransactionContext containing index and cumulative values, or null if not found + */ + private TransactionContext findTransactionContext(TransactionInfoList infoList, + ByteString txId) { + + long cumulativeGas = 0; + long cumulativeLogCount = 0; + + for (int index = 0; index < infoList.getTransactionInfoCount(); index++) { + TransactionInfo info = infoList.getTransactionInfo(index); + ResourceReceipt resourceReceipt = info.getReceipt(); + + if (info.getId().equals(txId)) { + return new TransactionContext(index, cumulativeGas, cumulativeLogCount); + } else { + cumulativeGas += resourceReceipt.getEnergyUsageTotal(); + cumulativeLogCount += info.getLogCount(); + } + } + return null; + } + + /** + * Get all transaction receipts for a specific block + * @param blockNumOrTag the block number or tag (latest, earliest, pending, finalized) + * @return List of TransactionReceipt objects for all transactions in the block, + * null if block not found + * @throws JsonRpcInvalidParamsException if the parameter format is invalid + * @throws JsonRpcInternalException if there's an internal error + */ + @Override + public List getBlockReceipts(String blockNumOrTag) + throws JsonRpcInvalidParamsException, JsonRpcInternalException { + Block block = wallet.getByJsonBlockId(blockNumOrTag); + if (block == null) { + return null; + } + + BlockCapsule blockCapsule = new BlockCapsule(block); + long blockNum = blockCapsule.getNum(); + TransactionInfoList transactionInfoList = wallet.getTransactionInfoByBlockNum(blockNum); + + //energy price at the block timestamp + long energyFee = wallet.getEnergyFee(blockCapsule.getTimeStamp()); + + // Validate transaction list size consistency + int transactionSizeInBlock = blockCapsule.getTransactions().size(); + if (transactionSizeInBlock != transactionInfoList.getTransactionInfoCount()) { + throw new JsonRpcInternalException( + String.format("TransactionList size mismatch: " + + "block has %d transactions, but transactionInfoList has %d", + transactionSizeInBlock, transactionInfoList.getTransactionInfoCount())); + } + + return getTransactionReceiptsFromBlock(blockCapsule, transactionInfoList, energyFee); + } + + /** + * Get all TransactionReceipts from a block + * This method processes all transactions in the block + * and creates receipts with cumulative gas calculations + * @param blockCapsule the block containing transactions + * @param transactionInfoList the transaction info list for the block + * @param energyFee the energy price at the block timestamp + * @return List of TransactionReceipt objects for all transactions in the block + */ + private List getTransactionReceiptsFromBlock(BlockCapsule blockCapsule, + TransactionInfoList transactionInfoList, long energyFee) { + + List receipts = new ArrayList<>(); + long cumulativeGas = 0; + long cumulativeLogCount = 0; + + for (int index = 0; index < transactionInfoList.getTransactionInfoCount(); index++) { + TransactionInfo info = transactionInfoList.getTransactionInfo(index); + ResourceReceipt resourceReceipt = info.getReceipt(); + + TransactionReceipt.TransactionContext context = new TransactionContext( + index, cumulativeGas, cumulativeLogCount); + + // Use the constructor with pre-calculated context + TransactionReceipt receipt = new TransactionReceipt(blockCapsule, info, context, energyFee); + receipts.add(receipt); + + cumulativeGas += resourceReceipt.getEnergyUsageTotal(); + cumulativeLogCount += info.getLogCount(); + } + return receipts; } @Override diff --git a/framework/src/main/java/org/tron/core/services/jsonrpc/types/TransactionReceipt.java b/framework/src/main/java/org/tron/core/services/jsonrpc/types/TransactionReceipt.java index 81b7c763cca..fd57ec0d9ad 100644 --- a/framework/src/main/java/org/tron/core/services/jsonrpc/types/TransactionReceipt.java +++ b/framework/src/main/java/org/tron/core/services/jsonrpc/types/TransactionReceipt.java @@ -9,179 +9,151 @@ import java.util.List; import lombok.Getter; import lombok.Setter; -import org.tron.api.GrpcAPI.TransactionInfoList; import org.tron.common.utils.ByteArray; -import org.tron.core.Wallet; import org.tron.core.capsule.BlockCapsule; import org.tron.core.capsule.TransactionCapsule; import org.tron.protos.Protocol; -import org.tron.protos.Protocol.ResourceReceipt; import org.tron.protos.Protocol.Transaction.Contract; import org.tron.protos.Protocol.Transaction.Contract.ContractType; import org.tron.protos.Protocol.TransactionInfo; +@Getter +@Setter @JsonPropertyOrder(alphabetic = true) public class TransactionReceipt { - @JsonPropertyOrder(alphabetic = true) + @Getter + @Setter public static class TransactionLog { - @Getter - @Setter private String logIndex; - @Getter - @Setter private String blockHash; - @Getter - @Setter private String blockNumber; - @Getter - @Setter private String transactionIndex; - @Getter - @Setter private String transactionHash; - @Getter - @Setter private String address; - @Getter - @Setter private String data; - @Getter - @Setter private String[] topics; - @Getter - @Setter private boolean removed = false; - public TransactionLog() { - } + public TransactionLog() {} } - @Getter - @Setter private String blockHash; - @Getter - @Setter private String blockNumber; - @Getter - @Setter private String transactionIndex; - @Getter - @Setter private String transactionHash; - @Getter - @Setter private String from; - @Getter - @Setter private String to; - @Getter - @Setter private String cumulativeGasUsed; - @Getter - @Setter private String effectiveGasPrice; - @Getter - @Setter private String gasUsed; - @Getter - @Setter private String contractAddress; - @Getter - @Setter private TransactionLog[] logs; - @Getter - @Setter - private String logsBloom; - @JsonInclude(JsonInclude.Include.NON_NULL) - public String root; // 32 bytes of post-transaction stateroot (pre Byzantium) - @JsonInclude(JsonInclude.Include.NON_NULL) - public String status; // either 1 (success) or 0 (failure) (post Byzantium) + private String logsBloom = ByteArray.toJsonHex(new byte[256]); // default no value; - @Getter - @Setter - private String type = "0x0"; - - public TransactionReceipt(Protocol.Block block, TransactionInfo txInfo, Wallet wallet) { - BlockCapsule blockCapsule = new BlockCapsule(block); - String txid = ByteArray.toHexString(txInfo.getId().toByteArray()); - long blockNum = blockCapsule.getNum(); - - Protocol.Transaction transaction = null; - long cumulativeGas = 0; - long cumulativeLogCount = 0; - - TransactionInfoList infoList = wallet.getTransactionInfoByBlockNum(blockNum); - for (int index = 0; index < infoList.getTransactionInfoCount(); index++) { - TransactionInfo info = infoList.getTransactionInfo(index); - ResourceReceipt resourceReceipt = info.getReceipt(); - - long energyUsage = resourceReceipt.getEnergyUsageTotal(); - cumulativeGas += energyUsage; - - if (ByteArray.toHexString(info.getId().toByteArray()).equals(txid)) { - transactionIndex = ByteArray.toJsonHex(index); - cumulativeGasUsed = ByteArray.toJsonHex(cumulativeGas); - gasUsed = ByteArray.toJsonHex(energyUsage); - status = resourceReceipt.getResultValue() <= 1 ? "0x1" : "0x0"; - - transaction = block.getTransactions(index); - break; - } else { - cumulativeLogCount += info.getLogCount(); - } - } - - blockHash = ByteArray.toJsonHex(blockCapsule.getBlockId().getBytes()); - blockNumber = ByteArray.toJsonHex(blockCapsule.getNum()); - transactionHash = ByteArray.toJsonHex(txInfo.getId().toByteArray()); - effectiveGasPrice = ByteArray.toJsonHex(wallet.getEnergyFee(blockCapsule.getTimeStamp())); - - from = null; - to = null; - contractAddress = null; + @JsonInclude(JsonInclude.Include.NON_NULL) + private String root = null; // 32 bytes of post-transaction stateroot (pre Byzantium) - if (transaction != null && !transaction.getRawData().getContractList().isEmpty()) { + @JsonInclude(JsonInclude.Include.NON_NULL) + private String status; // either 1 (success) or 0 (failure) (post Byzantium) + + private String type = "0x0"; // legacy transaction, set 0 in java-tron + + /** + * Constructor for creating a TransactionReceipt + * + * @param blockCapsule the block containing the transaction + * @param txInfo the transaction info containing execution details + * @param context the pre-calculated transaction context + * @param energyFee the energy price at the block timestamp + */ + public TransactionReceipt( + BlockCapsule blockCapsule, + TransactionInfo txInfo, + TransactionContext context, + long energyFee) { + // Set basic fields + this.blockHash = ByteArray.toJsonHex(blockCapsule.getBlockId().getBytes()); + this.blockNumber = ByteArray.toJsonHex(blockCapsule.getNum()); + this.transactionHash = ByteArray.toJsonHex(txInfo.getId().toByteArray()); + this.transactionIndex = ByteArray.toJsonHex(context.index); + // Compute cumulative gas until this transaction + this.cumulativeGasUsed = + ByteArray.toJsonHex(context.cumulativeGas + txInfo.getReceipt().getEnergyUsageTotal()); + this.gasUsed = ByteArray.toJsonHex(txInfo.getReceipt().getEnergyUsageTotal()); + this.status = txInfo.getReceipt().getResultValue() <= 1 ? "0x1" : "0x0"; + this.effectiveGasPrice = ByteArray.toJsonHex(energyFee); + + // Set contract fields + this.from = null; + this.to = null; + this.contractAddress = null; + + TransactionCapsule txCapsule = blockCapsule.getTransactions().get(context.index); + Protocol.Transaction transaction = txCapsule.getInstance(); + if (!transaction.getRawData().getContractList().isEmpty()) { Contract contract = transaction.getRawData().getContract(0); byte[] fromByte = TransactionCapsule.getOwner(contract); byte[] toByte = getToAddress(transaction); - from = ByteArray.toJsonHexAddress(fromByte); - to = ByteArray.toJsonHexAddress(toByte); + this.from = ByteArray.toJsonHexAddress(fromByte); + this.to = ByteArray.toJsonHexAddress(toByte); if (contract.getType() == ContractType.CreateSmartContract) { - contractAddress = ByteArray.toJsonHexAddress(txInfo.getContractAddress().toByteArray()); + this.contractAddress = + ByteArray.toJsonHexAddress(txInfo.getContractAddress().toByteArray()); } } - // logs + // Set logs List logList = new ArrayList<>(); - for (int index = 0; index < txInfo.getLogCount(); index++) { - TransactionInfo.Log log = txInfo.getLogList().get(index); - - TransactionReceipt.TransactionLog transactionLog = new TransactionReceipt.TransactionLog(); - // index is the index in the block - transactionLog.logIndex = ByteArray.toJsonHex(index + cumulativeLogCount); - transactionLog.transactionHash = transactionHash; - transactionLog.transactionIndex = transactionIndex; - transactionLog.blockHash = blockHash; - transactionLog.blockNumber = blockNumber; + for (int logIndex = 0; logIndex < txInfo.getLogCount(); logIndex++) { + TransactionInfo.Log log = txInfo.getLogList().get(logIndex); + TransactionLog transactionLog = new TransactionLog(); + transactionLog.setLogIndex(ByteArray.toJsonHex(logIndex + context.cumulativeLogCount)); + transactionLog.setTransactionHash(this.transactionHash); + transactionLog.setTransactionIndex(this.transactionIndex); + transactionLog.setBlockHash(this.blockHash); + transactionLog.setBlockNumber(this.blockNumber); + byte[] addressByte = convertToTronAddress(log.getAddress().toByteArray()); - transactionLog.address = ByteArray.toJsonHexAddress(addressByte); - transactionLog.data = ByteArray.toJsonHex(log.getData().toByteArray()); + transactionLog.setAddress(ByteArray.toJsonHexAddress(addressByte)); + transactionLog.setData(ByteArray.toJsonHex(log.getData().toByteArray())); String[] topics = new String[log.getTopicsCount()]; for (int i = 0; i < log.getTopicsCount(); i++) { topics[i] = ByteArray.toJsonHex(log.getTopics(i).toByteArray()); } - transactionLog.topics = topics; + transactionLog.setTopics(topics); logList.add(transactionLog); } + this.logs = logList.toArray(new TransactionLog[0]); + + } - logs = logList.toArray(new TransactionReceipt.TransactionLog[logList.size()]); - logsBloom = ByteArray.toJsonHex(new byte[256]); // no value - root = null; + /** + * Context class to hold transaction creation parameters Contains index and cumulative values + * needed for receipt creation + */ + @Getter + public static class TransactionContext { + private final int index; + private final long cumulativeGas; + private final long cumulativeLogCount; + + /** + * Creates a transaction context with the given parameters + * + * @param index the transaction index within the block + * @param cumulativeGas the cumulative gas used up to this transaction + * @param cumulativeLogCount the cumulative log count up to this transaction + */ + public TransactionContext(int index, long cumulativeGas, long cumulativeLogCount) { + this.index = index; + this.cumulativeGas = cumulativeGas; + this.cumulativeLogCount = cumulativeLogCount; + } } -} \ No newline at end of file +} diff --git a/framework/src/test/java/org/tron/core/jsonrpc/JsonrpcServiceTest.java b/framework/src/test/java/org/tron/core/jsonrpc/JsonrpcServiceTest.java index f3069faead9..f6f4a35c1b7 100644 --- a/framework/src/test/java/org/tron/core/jsonrpc/JsonrpcServiceTest.java +++ b/framework/src/test/java/org/tron/core/jsonrpc/JsonrpcServiceTest.java @@ -1,6 +1,7 @@ package org.tron.core.jsonrpc; import static org.tron.core.services.jsonrpc.JsonRpcApiUtil.getByJsonBlockId; +import static org.tron.core.services.jsonrpc.TronJsonRpcImpl.TAG_PENDING_SUPPORT_ERROR; import com.alibaba.fastjson.JSON; import com.google.gson.JsonArray; @@ -33,8 +34,11 @@ import org.tron.core.capsule.AccountCapsule; import org.tron.core.capsule.BlockCapsule; import org.tron.core.capsule.TransactionCapsule; +import org.tron.core.capsule.TransactionInfoCapsule; +import org.tron.core.capsule.TransactionRetCapsule; import org.tron.core.capsule.utils.BlockUtil; import org.tron.core.config.args.Args; +import org.tron.core.exception.jsonrpc.JsonRpcInternalException; import org.tron.core.exception.jsonrpc.JsonRpcInvalidParamsException; import org.tron.core.services.NodeInfoService; import org.tron.core.services.interfaceJsonRpcOnPBFT.JsonRpcServiceOnPBFT; @@ -44,6 +48,7 @@ import org.tron.core.services.jsonrpc.TronJsonRpcImpl; import org.tron.core.services.jsonrpc.filters.LogFilterWrapper; import org.tron.core.services.jsonrpc.types.BlockResult; +import org.tron.core.services.jsonrpc.types.TransactionReceipt; import org.tron.core.services.jsonrpc.types.TransactionResult; import org.tron.protos.Protocol; import org.tron.protos.Protocol.Transaction.Contract.ContractType; @@ -127,6 +132,7 @@ public void init() { (Wallet.getAddressPreFixString() + "ED738B3A0FE390EAA71B768B6D02CDBD18FB207B")))) .build(); + transactionCapsule1 = new TransactionCapsule(transferContract1, ContractType.TransferContract); transactionCapsule1.setBlockNum(blockCapsule1.getNum()); TransactionCapsule transactionCapsule2 = new TransactionCapsule(transferContract2, @@ -155,6 +161,59 @@ public void init() { dbManager.getTransactionStore() .put(transactionCapsule3.getTransactionId().getBytes(), transactionCapsule3); + dbManager.getTransactionStore() + .put(transactionCapsule3.getTransactionId().getBytes(), transactionCapsule3); + + blockCapsule0.getTransactions().forEach(tx -> { + TransactionCapsule transactionCapsule = new TransactionCapsule(tx.getInstance()); + transactionCapsule.setBlockNum(blockCapsule0.getNum()); + dbManager.getTransactionStore() + .put(transactionCapsule.getTransactionId().getBytes(), transactionCapsule); + }); + + TransactionRetCapsule transactionRetCapsule0 = new TransactionRetCapsule(); + blockCapsule0.getTransactions().forEach(tx -> { + TransactionInfoCapsule transactionInfoCapsule = new TransactionInfoCapsule(); + transactionInfoCapsule.setId(tx.getTransactionId().getBytes()); + transactionInfoCapsule.setBlockNumber(blockCapsule0.getNum()); + transactionRetCapsule0.addTransactionInfo(transactionInfoCapsule.getInstance()); + }); + dbManager.getTransactionRetStore().put( + ByteArray.fromLong(blockCapsule0.getNum()), transactionRetCapsule0); + + List logs = new ArrayList<>(); + logs.add(Protocol.TransactionInfo.Log.newBuilder() + .setAddress(ByteString.copyFrom("address1".getBytes())) + .setData(ByteString.copyFrom("data1".getBytes())) + .addTopics(ByteString.copyFrom("topic1".getBytes())) + .build()); + logs.add(Protocol.TransactionInfo.Log.newBuilder() + .setAddress(ByteString.copyFrom("address2".getBytes())) + .setData(ByteString.copyFrom("data2".getBytes())) + .addTopics(ByteString.copyFrom("topic2".getBytes())) + .build()); + + TransactionRetCapsule transactionRetCapsule1 = new TransactionRetCapsule(); + blockCapsule1.getTransactions().forEach(tx -> { + TransactionInfoCapsule transactionInfoCapsule = new TransactionInfoCapsule(); + transactionInfoCapsule.setId(tx.getTransactionId().getBytes()); + transactionInfoCapsule.setBlockNumber(blockCapsule1.getNum()); + transactionInfoCapsule.addAllLog(logs); + transactionRetCapsule1.addTransactionInfo(transactionInfoCapsule.getInstance()); + }); + dbManager.getTransactionRetStore() + .put(ByteArray.fromLong(blockCapsule1.getNum()), transactionRetCapsule1); + + TransactionRetCapsule transactionRetCapsule2 = new TransactionRetCapsule(); + blockCapsule2.getTransactions().forEach(tx -> { + TransactionInfoCapsule transactionInfoCapsule = new TransactionInfoCapsule(); + transactionInfoCapsule.setId(tx.getTransactionId().getBytes()); + transactionInfoCapsule.setBlockNumber(blockCapsule2.getNum()); + transactionRetCapsule2.addTransactionInfo(transactionInfoCapsule.getInstance()); + }); + dbManager.getTransactionRetStore() + .put(ByteArray.fromLong(blockCapsule2.getNum()), transactionRetCapsule2); + tronJsonRpc = new TronJsonRpcImpl(nodeInfoService, wallet, dbManager); } @@ -970,6 +1029,67 @@ public void testNewFilterFinalizedBlock() { } } + @Test + public void testGetBlockReceipts() { + + try { + List transactionReceiptList = tronJsonRpc.getBlockReceipts("0x2710"); + Assert.assertFalse(transactionReceiptList.isEmpty()); + for (TransactionReceipt transactionReceipt: transactionReceiptList) { + TransactionReceipt transactionReceipt1 + = tronJsonRpc.getTransactionReceipt(transactionReceipt.getTransactionHash()); + + Assert.assertEquals( + JSON.toJSONString(transactionReceipt), JSON.toJSONString(transactionReceipt1)); + } + } catch (JsonRpcInvalidParamsException | JsonRpcInternalException e) { + throw new RuntimeException(e); + } + + try { + List transactionReceiptList = tronJsonRpc.getBlockReceipts("earliest"); + Assert.assertFalse(transactionReceiptList.isEmpty()); + } catch (JsonRpcInvalidParamsException | JsonRpcInternalException e) { + throw new RuntimeException(e); + } + + try { + List transactionReceiptList = tronJsonRpc.getBlockReceipts("latest"); + Assert.assertFalse(transactionReceiptList.isEmpty()); + } catch (JsonRpcInvalidParamsException | JsonRpcInternalException e) { + throw new RuntimeException(e); + } + + try { + List transactionReceiptList = tronJsonRpc.getBlockReceipts("finalized"); + Assert.assertFalse(transactionReceiptList.isEmpty()); + } catch (JsonRpcInvalidParamsException | JsonRpcInternalException e) { + throw new RuntimeException(e); + } + + try { + tronJsonRpc.getBlockReceipts("pending"); + Assert.fail(); + } catch (Exception e) { + Assert.assertEquals(TAG_PENDING_SUPPORT_ERROR, e.getMessage()); + } + + try { + tronJsonRpc.getBlockReceipts("test"); + Assert.fail(); + } catch (Exception e) { + Assert.assertEquals("invalid block number", e.getMessage()); + } + + try { + List transactionReceiptList = tronJsonRpc.getBlockReceipts("0x2"); + Assert.assertNull(transactionReceiptList); + } catch (JsonRpcInvalidParamsException | JsonRpcInternalException e) { + throw new RuntimeException(e); + } + + } + @Test public void testWeb3ClientVersion() { try { diff --git a/framework/src/test/java/org/tron/core/services/jsonrpc/TransactionReceiptTest.java b/framework/src/test/java/org/tron/core/services/jsonrpc/TransactionReceiptTest.java index f10526e30a4..23bc11e293f 100644 --- a/framework/src/test/java/org/tron/core/services/jsonrpc/TransactionReceiptTest.java +++ b/framework/src/test/java/org/tron/core/services/jsonrpc/TransactionReceiptTest.java @@ -8,31 +8,32 @@ import org.tron.common.utils.ByteArray; import org.tron.core.Constant; import org.tron.core.Wallet; +import org.tron.core.capsule.BlockCapsule; import org.tron.core.capsule.TransactionRetCapsule; import org.tron.core.config.args.Args; +import org.tron.core.exception.jsonrpc.JsonRpcInternalException; import org.tron.core.services.jsonrpc.types.TransactionReceipt; +import org.tron.core.services.jsonrpc.types.TransactionReceipt.TransactionContext; import org.tron.core.store.TransactionRetStore; import org.tron.protos.Protocol; public class TransactionReceiptTest extends BaseTest { - @Resource - private Wallet wallet; + @Resource private Wallet wallet; - @Resource - private TransactionRetStore transactionRetStore; + @Resource private TransactionRetStore transactionRetStore; static { - Args.setParam(new String[]{"-d", dbPath()}, Constant.TEST_CONF); + Args.setParam(new String[] {"-d", dbPath()}, Constant.TEST_CONF); } @Test - public void testTransactionReceipt() { + public void testTransactionReceipt() throws JsonRpcInternalException { Protocol.TransactionInfo transactionInfo = Protocol.TransactionInfo.newBuilder() .setId(ByteString.copyFrom("1".getBytes())) .setContractAddress(ByteString.copyFrom("address1".getBytes())) .setReceipt(Protocol.ResourceReceipt.newBuilder() - .setEnergyUsageTotal(0L) + .setEnergyUsageTotal(100L) .setResult(Protocol.Transaction.Result.contractResult.DEFAULT) .build()) .addLog(Protocol.TransactionInfo.Log.newBuilder() @@ -50,17 +51,49 @@ public void testTransactionReceipt() { raw.addContract(contract.build()); Protocol.Transaction transaction = Protocol.Transaction.newBuilder().setRawData(raw).build(); - TransactionReceipt transactionReceipt = new TransactionReceipt( - Protocol.Block.newBuilder().setBlockHeader( - Protocol.BlockHeader.newBuilder().setRawData( - Protocol.BlockHeader.raw.newBuilder().setNumber(1))).addTransactions( - transaction).build(), transactionInfo, wallet); + Protocol.Block block = Protocol.Block.newBuilder().setBlockHeader( + Protocol.BlockHeader.newBuilder().setRawData( + Protocol.BlockHeader.raw.newBuilder().setNumber(1))).addTransactions( + transaction).build(); - Assert.assertEquals(transactionReceipt.getBlockNumber(),"0x1"); - Assert.assertEquals(transactionReceipt.getTransactionIndex(),"0x0"); - Assert.assertEquals(transactionReceipt.getLogs().length,1); - Assert.assertEquals(transactionReceipt.getBlockHash(), - "0x0000000000000001464f071c8a336fd22eb5145dff1b245bda013ec89add8497"); - } + BlockCapsule blockCapsule = new BlockCapsule(block); + long energyFee = wallet.getEnergyFee(blockCapsule.getTimeStamp()); + TransactionReceipt.TransactionContext context + = new TransactionContext(0, 2, 3); + + TransactionReceipt transactionReceipt = + new TransactionReceipt(blockCapsule, transactionInfo, context, energyFee); + + Assert.assertNotNull(transactionReceipt); + String blockHash = "0x0000000000000001464f071c8a336fd22eb5145dff1b245bda013ec89add8497"; + + // assert basic fields + Assert.assertEquals(transactionReceipt.getBlockHash(), blockHash); + Assert.assertEquals(transactionReceipt.getBlockNumber(), "0x1"); + Assert.assertEquals(transactionReceipt.getTransactionHash(), "0x31"); + Assert.assertEquals(transactionReceipt.getTransactionIndex(), "0x0"); + Assert.assertEquals(transactionReceipt.getCumulativeGasUsed(), ByteArray.toJsonHex(102)); + Assert.assertEquals(transactionReceipt.getGasUsed(), ByteArray.toJsonHex(100)); + Assert.assertEquals(transactionReceipt.getEffectiveGasPrice(), ByteArray.toJsonHex(energyFee)); + Assert.assertEquals(transactionReceipt.getStatus(), "0x1"); + // assert contract fields + Assert.assertEquals(transactionReceipt.getFrom(), ByteArray.toJsonHexAddress(new byte[0])); + Assert.assertEquals(transactionReceipt.getTo(), ByteArray.toJsonHexAddress(new byte[0])); + Assert.assertNull(transactionReceipt.getContractAddress()); + + // assert logs fields + Assert.assertEquals(transactionReceipt.getLogs().length, 1); + Assert.assertEquals(transactionReceipt.getLogs()[0].getLogIndex(), "0x3"); + Assert.assertEquals( + transactionReceipt.getLogs()[0].getBlockHash(), blockHash); + Assert.assertEquals(transactionReceipt.getLogs()[0].getBlockNumber(), "0x1"); + Assert.assertEquals(transactionReceipt.getLogs()[0].getTransactionHash(), "0x31"); + Assert.assertEquals(transactionReceipt.getLogs()[0].getTransactionIndex(), "0x0"); + + // assert default fields + Assert.assertNull(transactionReceipt.getRoot()); + Assert.assertEquals(transactionReceipt.getType(), "0x0"); + Assert.assertEquals(transactionReceipt.getLogsBloom(), ByteArray.toJsonHex(new byte[256])); + } } From f7a6748fd8444d349f2ca05910554b5908810364 Mon Sep 17 00:00:00 2001 From: Federico2014 Date: Tue, 19 Aug 2025 18:02:05 +0800 Subject: [PATCH 31/59] fix(crypto): resolve the invalid witness (#6368) * feat(crypto): upgrade the bouncycastle denpendency * fix(crypto): optimize the null localwitness * fix(metric): process the null witness address * feat(crypto): optimize the code * feat(doc): add the comment * feat(test): add the nullWitnessAddress test * add witness config test * strengthen the private key validation * increase the test coverage * optimiz the witnessAddress initialization * simplify the localWitness * refactor the code * add the test unit --- build.gradle | 2 +- .../org/tron/common/utils/LocalWitnesses.java | 33 +-- .../org/tron/common/utils/StringUtil.java | 17 ++ .../java/org/tron/common/crypto/ECKey.java | 3 +- .../java/org/tron/common/crypto/sm2/SM2.java | 3 +- .../java/org/tron/core/config/args/Args.java | 74 +---- .../core/config/args/WitnessInitializer.java | 149 ++++++++++ .../tron/core/consensus/ConsensusService.java | 7 +- .../core/metrics/node/NodeMetricManager.java | 7 +- .../core/net/service/relay/RelayService.java | 8 +- .../org/tron/common/crypto/ECKeyTest.java | 6 + .../org/tron/common/crypto/SM2KeyTest.java | 6 + .../tron/core/capsule/BlockCapsuleTest.java | 2 +- .../org/tron/core/config/args/ArgsTest.java | 6 +- .../core/config/args/LocalWitnessTest.java | 139 ++++++++- .../config/args/WitnessInitializerTest.java | 268 ++++++++++++++++++ .../java/org/tron/core/db/ManagerTest.java | 2 +- .../tron/core/db/TransactionExpireTest.java | 10 +- .../core/net/services/RelayServiceTest.java | 31 +- .../core/zksnark/ShieldedReceiveTest.java | 2 +- gradle/verification-metadata.xml | 10 +- plugins/build.gradle | 4 +- 22 files changed, 658 insertions(+), 131 deletions(-) create mode 100644 framework/src/main/java/org/tron/core/config/args/WitnessInitializer.java create mode 100644 framework/src/test/java/org/tron/core/config/args/WitnessInitializerTest.java diff --git a/build.gradle b/build.gradle index e66ee616807..8a9dd8bec75 100644 --- a/build.gradle +++ b/build.gradle @@ -84,7 +84,7 @@ subprojects { implementation group: 'org.apache.commons', name: 'commons-math', version: '2.2' implementation "org.apache.commons:commons-collections4:4.1" implementation group: 'joda-time', name: 'joda-time', version: '2.3' - implementation group: 'org.bouncycastle', name: 'bcprov-jdk15on', version: '1.69' + implementation group: 'org.bouncycastle', name: 'bcprov-jdk18on', version: '1.79' compileOnly 'org.projectlombok:lombok:1.18.34' annotationProcessor 'org.projectlombok:lombok:1.18.34' diff --git a/chainbase/src/main/java/org/tron/common/utils/LocalWitnesses.java b/chainbase/src/main/java/org/tron/common/utils/LocalWitnesses.java index 940a107a2ac..812e4d7fa5b 100644 --- a/chainbase/src/main/java/org/tron/common/utils/LocalWitnesses.java +++ b/chainbase/src/main/java/org/tron/common/utils/LocalWitnesses.java @@ -32,6 +32,7 @@ public class LocalWitnesses { @Getter private List privateKeys = Lists.newArrayList(); + @Getter private byte[] witnessAccountAddress; public LocalWitnesses() { @@ -45,21 +46,11 @@ public LocalWitnesses(List privateKeys) { setPrivateKeys(privateKeys); } - public byte[] getWitnessAccountAddress(boolean isECKeyCryptoEngine) { - if (witnessAccountAddress == null) { - byte[] privateKey = ByteArray.fromHexString(getPrivateKey()); - final SignInterface cryptoEngine = SignUtils.fromPrivate(privateKey, isECKeyCryptoEngine); - this.witnessAccountAddress = cryptoEngine.getAddress(); - } - return witnessAccountAddress; - } - - public void setWitnessAccountAddress(final byte[] localWitnessAccountAddress) { - this.witnessAccountAddress = localWitnessAccountAddress; - } - - public void initWitnessAccountAddress(boolean isECKeyCryptoEngine) { - if (witnessAccountAddress == null) { + public void initWitnessAccountAddress(final byte[] witnessAddress, + boolean isECKeyCryptoEngine) { + if (witnessAddress != null) { + this.witnessAccountAddress = witnessAddress; + } else if (!CollectionUtils.isEmpty(privateKeys)) { byte[] privateKey = ByteArray.fromHexString(getPrivateKey()); final SignInterface ecKey = SignUtils.fromPrivate(privateKey, isECKeyCryptoEngine); @@ -85,11 +76,15 @@ private void validate(String privateKey) { privateKey = privateKey.substring(2); } - if (StringUtils.isNotBlank(privateKey) - && privateKey.length() != ChainConstant.PRIVATE_KEY_LENGTH) { + if (StringUtils.isBlank(privateKey) + || privateKey.length() != ChainConstant.PRIVATE_KEY_LENGTH) { throw new IllegalArgumentException( - String.format("private key must be %d-bits hex string, actual: %d", - ChainConstant.PRIVATE_KEY_LENGTH, privateKey.length())); + String.format("private key must be %d hex string, actual: %d", + ChainConstant.PRIVATE_KEY_LENGTH, + StringUtils.isBlank(privateKey) ? 0 : privateKey.length())); + } + if (!StringUtil.isHexadecimal(privateKey)) { + throw new IllegalArgumentException("private key must be hex string"); } } diff --git a/common/src/main/java/org/tron/common/utils/StringUtil.java b/common/src/main/java/org/tron/common/utils/StringUtil.java index ce3e95af46e..412a70d7f9c 100644 --- a/common/src/main/java/org/tron/common/utils/StringUtil.java +++ b/common/src/main/java/org/tron/common/utils/StringUtil.java @@ -44,4 +44,21 @@ public static String createReadableString(ByteString string) { public static ByteString hexString2ByteString(String hexString) { return ByteString.copyFrom(ByteArray.fromHexString(hexString)); } + + public static boolean isHexadecimal(String str) { + if (str == null || str.isEmpty()) { + return false; + } + if (str.length() % 2 != 0) { + return false; + } + + for (int i = 0; i < str.length(); i++) { + char c = str.charAt(i); + if (Character.digit(c, 16) == -1) { + return false; + } + } + return true; + } } diff --git a/crypto/src/main/java/org/tron/common/crypto/ECKey.java b/crypto/src/main/java/org/tron/common/crypto/ECKey.java index 5fe8b9ef359..d0a6048aca1 100644 --- a/crypto/src/main/java/org/tron/common/crypto/ECKey.java +++ b/crypto/src/main/java/org/tron/common/crypto/ECKey.java @@ -55,6 +55,7 @@ import org.tron.common.crypto.jce.ECKeyPairGenerator; import org.tron.common.crypto.jce.TronCastleProvider; import org.tron.common.utils.BIUtil; +import org.tron.common.utils.ByteArray; import org.tron.common.utils.ByteUtil; @Slf4j(topic = "crypto") @@ -285,7 +286,7 @@ public static ECKey fromPrivate(BigInteger privKey) { * @return - */ public static ECKey fromPrivate(byte[] privKeyBytes) { - if (Objects.isNull(privKeyBytes)) { + if (ByteArray.isEmpty(privKeyBytes)) { return null; } return fromPrivate(new BigInteger(1, privKeyBytes)); diff --git a/crypto/src/main/java/org/tron/common/crypto/sm2/SM2.java b/crypto/src/main/java/org/tron/common/crypto/sm2/SM2.java index c6aebba385a..b1d349efad3 100644 --- a/crypto/src/main/java/org/tron/common/crypto/sm2/SM2.java +++ b/crypto/src/main/java/org/tron/common/crypto/sm2/SM2.java @@ -41,6 +41,7 @@ import org.tron.common.crypto.SignatureInterface; import org.tron.common.crypto.jce.ECKeyFactory; import org.tron.common.crypto.jce.TronCastleProvider; +import org.tron.common.utils.ByteArray; import org.tron.common.utils.ByteUtil; /** @@ -247,7 +248,7 @@ public static SM2 fromPrivate(BigInteger privKey) { * @return - */ public static SM2 fromPrivate(byte[] privKeyBytes) { - if (Objects.isNull(privKeyBytes)) { + if (ByteArray.isEmpty(privKeyBytes)) { return null; } return fromPrivate(new BigInteger(1, privKeyBytes)); diff --git a/framework/src/main/java/org/tron/core/config/args/Args.java b/framework/src/main/java/org/tron/core/config/args/Args.java index 3c162e03549..92a021280c4 100644 --- a/framework/src/main/java/org/tron/core/config/args/Args.java +++ b/framework/src/main/java/org/tron/core/config/args/Args.java @@ -53,7 +53,6 @@ import org.tron.common.args.Witness; import org.tron.common.config.DbBackupConfig; import org.tron.common.cron.CronExpression; -import org.tron.common.crypto.SignInterface; import org.tron.common.logsfilter.EventPluginConfig; import org.tron.common.logsfilter.FilterQuery; import org.tron.common.logsfilter.TriggerConfig; @@ -62,7 +61,6 @@ import org.tron.common.parameter.CommonParameter; import org.tron.common.parameter.RateLimiterInitialization; import org.tron.common.setting.RocksDbSettings; -import org.tron.common.utils.ByteArray; import org.tron.common.utils.Commons; import org.tron.common.utils.LocalWitnesses; import org.tron.core.Constant; @@ -70,11 +68,8 @@ import org.tron.core.config.Configuration; import org.tron.core.config.Parameter.NetConstants; import org.tron.core.config.Parameter.NodeConstant; -import org.tron.core.exception.CipherException; import org.tron.core.exception.TronError; import org.tron.core.store.AccountStore; -import org.tron.keystore.Credentials; -import org.tron.keystore.WalletUtils; import org.tron.p2p.P2pConfig; import org.tron.p2p.dns.update.DnsType; import org.tron.p2p.dns.update.PublishConfig; @@ -412,61 +407,7 @@ public static void setParam(final Config config) { PARAMETER.cryptoEngine = config.hasPath(Constant.CRYPTO_ENGINE) ? config .getString(Constant.CRYPTO_ENGINE) : Constant.ECKey_ENGINE; - 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); - } - } - localWitnesses.initWitnessAccountAddress(PARAMETER.isECKeyCryptoEngine()); - logger.debug("Got privateKey from cmd"); - } else if (config.hasPath(Constant.LOCAL_WITNESS)) { - localWitnesses = new LocalWitnesses(); - List 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)) { - localWitnesses = new LocalWitnesses(); - List privateKeys = new ArrayList(); - if (PARAMETER.isWitness()) { - List 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("Witness node start failed!"); - throw new TronError(e, TronError.ErrCode.WITNESS_KEYSTORE_LOAD); - } - } - } - localWitnesses.setPrivateKeys(privateKeys); - witnessAddressCheck(config); - localWitnesses.initWitnessAccountAddress(PARAMETER.isECKeyCryptoEngine()); - logger.debug("Got privateKey from keystore"); - } - + localWitnesses = new WitnessInitializer(config).initLocalWitnesses(); if (PARAMETER.isWitness() && CollectionUtils.isEmpty(localWitnesses.getPrivateKeys())) { throw new TronError("This is a witness node, but localWitnesses is null", @@ -1817,19 +1758,6 @@ public static void logConfig() { logger.info("\n"); } - private static void witnessAddressCheck(Config config) { - if (config.hasPath(Constant.LOCAL_WITNESS_ACCOUNT_ADDRESS)) { - byte[] bytes = Commons - .decodeFromBase58Check(config.getString(Constant.LOCAL_WITNESS_ACCOUNT_ADDRESS)); - if (bytes != null) { - localWitnesses.setWitnessAccountAddress(bytes); - logger.debug("Got localWitnessAccountAddress from config.conf"); - } else { - logger.warn(IGNORE_WRONG_WITNESS_ADDRESS_FORMAT); - } - } - } - /** * get output directory. */ diff --git a/framework/src/main/java/org/tron/core/config/args/WitnessInitializer.java b/framework/src/main/java/org/tron/core/config/args/WitnessInitializer.java new file mode 100644 index 00000000000..2ea3a449ef4 --- /dev/null +++ b/framework/src/main/java/org/tron/core/config/args/WitnessInitializer.java @@ -0,0 +1,149 @@ +package org.tron.core.config.args; + +import com.typesafe.config.Config; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.tron.common.crypto.SignInterface; +import org.tron.common.utils.ByteArray; +import org.tron.common.utils.Commons; +import org.tron.common.utils.LocalWitnesses; +import org.tron.core.Constant; +import org.tron.core.exception.CipherException; +import org.tron.core.exception.TronError; +import org.tron.keystore.Credentials; +import org.tron.keystore.WalletUtils; + +@Slf4j +public class WitnessInitializer { + + private final Config config; + + private LocalWitnesses localWitnesses; + + public WitnessInitializer(Config config) { + this.config = config; + this.localWitnesses = new LocalWitnesses(); + } + + public LocalWitnesses initLocalWitnesses() { + if (!Args.PARAMETER.isWitness()) { + return localWitnesses; + } + + if (tryInitFromCommandLine()) { + return localWitnesses; + } + + if (tryInitFromConfig()) { + return localWitnesses; + } + + tryInitFromKeystore(); + + return localWitnesses; + } + + private boolean tryInitFromCommandLine() { + if (StringUtils.isBlank(Args.PARAMETER.privateKey)) { + return false; + } + + byte[] witnessAddress = null; + this.localWitnesses = new LocalWitnesses(Args.PARAMETER.privateKey); + if (StringUtils.isNotEmpty(Args.PARAMETER.witnessAddress)) { + witnessAddress = Commons.decodeFromBase58Check(Args.PARAMETER.witnessAddress); + if (witnessAddress == null) { + throw new TronError("LocalWitnessAccountAddress format from cmd is incorrect", + TronError.ErrCode.WITNESS_INIT); + } + logger.debug("Got localWitnessAccountAddress from cmd"); + } + + this.localWitnesses.initWitnessAccountAddress(witnessAddress, + Args.PARAMETER.isECKeyCryptoEngine()); + logger.debug("Got privateKey from cmd"); + return true; + } + + private boolean tryInitFromConfig() { + if (!config.hasPath(Constant.LOCAL_WITNESS) || config.getStringList(Constant.LOCAL_WITNESS) + .isEmpty()) { + return false; + } + + List localWitness = config.getStringList(Constant.LOCAL_WITNESS); + this.localWitnesses.setPrivateKeys(localWitness); + logger.debug("Got privateKey from config.conf"); + byte[] witnessAddress = getWitnessAddress(); + this.localWitnesses.initWitnessAccountAddress(witnessAddress, + Args.PARAMETER.isECKeyCryptoEngine()); + return true; + } + + private void tryInitFromKeystore() { + if (!config.hasPath(Constant.LOCAL_WITNESS_KEYSTORE) + || config.getStringList(Constant.LOCAL_WITNESS_KEYSTORE).isEmpty()) { + return; + } + + List localWitness = config.getStringList(Constant.LOCAL_WITNESS_KEYSTORE); + if (localWitness.size() > 1) { + logger.warn( + "Multiple keystores detected. Only the first keystore will be used as witness, all " + + "others will be ignored."); + } + + List privateKeys = new ArrayList<>(); + String fileName = System.getProperty("user.dir") + "/" + localWitness.get(0); + String password; + if (StringUtils.isEmpty(Args.PARAMETER.password)) { + System.out.println("Please input your password."); + password = WalletUtils.inputPassword(); + } else { + password = Args.PARAMETER.password; + Args.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("Witness node start failed!"); + throw new TronError(e, TronError.ErrCode.WITNESS_KEYSTORE_LOAD); + } + + this.localWitnesses.setPrivateKeys(privateKeys); + byte[] witnessAddress = getWitnessAddress(); + this.localWitnesses.initWitnessAccountAddress(witnessAddress, + Args.PARAMETER.isECKeyCryptoEngine()); + logger.debug("Got privateKey from keystore"); + } + + private byte[] getWitnessAddress() { + if (!config.hasPath(Constant.LOCAL_WITNESS_ACCOUNT_ADDRESS)) { + return null; + } + + if (localWitnesses.getPrivateKeys().size() != 1) { + throw new TronError( + "LocalWitnessAccountAddress can only be set when there is only one private key", + TronError.ErrCode.WITNESS_INIT); + } + byte[] witnessAddress = Commons + .decodeFromBase58Check(config.getString(Constant.LOCAL_WITNESS_ACCOUNT_ADDRESS)); + if (witnessAddress != null) { + logger.debug("Got localWitnessAccountAddress from config.conf"); + } else { + throw new TronError("LocalWitnessAccountAddress format from config is incorrect", + TronError.ErrCode.WITNESS_INIT); + } + return witnessAddress; + } +} diff --git a/framework/src/main/java/org/tron/core/consensus/ConsensusService.java b/framework/src/main/java/org/tron/core/consensus/ConsensusService.java index ce1f1f1cf08..ef8f30ef498 100644 --- a/framework/src/main/java/org/tron/core/consensus/ConsensusService.java +++ b/framework/src/main/java/org/tron/core/consensus/ConsensusService.java @@ -61,17 +61,18 @@ public void start() { logger.info("Add witness: {}, size: {}", Hex.toHexString(privateKeyAddress), miners.size()); } - } else { + } else if (privateKeys.size() == 1) { byte[] privateKey = fromHexString(Args.getLocalWitnesses().getPrivateKey()); byte[] privateKeyAddress = SignUtils.fromPrivate(privateKey, Args.getInstance().isECKeyCryptoEngine()).getAddress(); - byte[] witnessAddress = Args.getLocalWitnesses().getWitnessAccountAddress( - Args.getInstance().isECKeyCryptoEngine()); + byte[] witnessAddress = Args.getLocalWitnesses().getWitnessAccountAddress(); WitnessCapsule witnessCapsule = witnessStore.get(witnessAddress); if (null == witnessCapsule) { logger.warn("Witness {} is not in witnessStore.", Hex.toHexString(witnessAddress)); } + // In multi-signature mode, the address derived from the private key may differ from + // witnessAddress. Miner miner = param.new Miner(privateKey, ByteString.copyFrom(privateKeyAddress), ByteString.copyFrom(witnessAddress)); miners.add(miner); diff --git a/framework/src/main/java/org/tron/core/metrics/node/NodeMetricManager.java b/framework/src/main/java/org/tron/core/metrics/node/NodeMetricManager.java index a0aba129648..9a5ecb33213 100644 --- a/framework/src/main/java/org/tron/core/metrics/node/NodeMetricManager.java +++ b/framework/src/main/java/org/tron/core/metrics/node/NodeMetricManager.java @@ -4,7 +4,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.tron.common.backup.BackupManager; -import org.tron.common.parameter.CommonParameter; +import org.tron.common.utils.ByteArray; import org.tron.core.ChainBaseManager; import org.tron.core.config.args.Args; import org.tron.program.Version; @@ -36,8 +36,9 @@ private void setNodeInfo(NodeInfo nodeInfo) { nodeInfo.setIp(Args.getInstance().getNodeExternalIp()); - ByteString witnessAddress = ByteString.copyFrom(Args.getLocalWitnesses() - .getWitnessAccountAddress(CommonParameter.getInstance().isECKeyCryptoEngine())); + byte[] witnessAccountAddress = Args.getLocalWitnesses().getWitnessAccountAddress(); + ByteString witnessAddress = !ByteArray.isEmpty(witnessAccountAddress) ? ByteString + .copyFrom(witnessAccountAddress) : null; if (chainBaseManager.getWitnessScheduleStore().getActiveWitnesses().contains(witnessAddress)) { nodeInfo.setNodeType(1); } else { diff --git a/framework/src/main/java/org/tron/core/net/service/relay/RelayService.java b/framework/src/main/java/org/tron/core/net/service/relay/RelayService.java index 161e918336b..61ae6326e9f 100644 --- a/framework/src/main/java/org/tron/core/net/service/relay/RelayService.java +++ b/framework/src/main/java/org/tron/core/net/service/relay/RelayService.java @@ -66,11 +66,11 @@ public class RelayService { private List fastForwardNodes = parameter.getFastForwardNodes(); - private ByteString witnessAddress = ByteString - .copyFrom(Args.getLocalWitnesses().getWitnessAccountAddress(CommonParameter.getInstance() - .isECKeyCryptoEngine())); + private final int keySize = Args.getLocalWitnesses().getPrivateKeys().size(); - private int keySize = Args.getLocalWitnesses().getPrivateKeys().size(); + private final ByteString witnessAddress = + Args.getLocalWitnesses().getWitnessAccountAddress() != null ? ByteString + .copyFrom(Args.getLocalWitnesses().getWitnessAccountAddress()) : null; private int maxFastForwardNum = Args.getInstance().getMaxFastForwardNum(); diff --git a/framework/src/test/java/org/tron/common/crypto/ECKeyTest.java b/framework/src/test/java/org/tron/common/crypto/ECKeyTest.java index 4e7d45ee8d7..273672e8342 100644 --- a/framework/src/test/java/org/tron/common/crypto/ECKeyTest.java +++ b/framework/src/test/java/org/tron/common/crypto/ECKeyTest.java @@ -5,6 +5,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.tron.common.utils.client.utils.AbiUtil.generateOccupationConstantPrivateKey; @@ -67,6 +68,11 @@ public void testFromPrivateKey() { assertTrue(key.isPubKeyCanonical()); assertTrue(key.hasPrivKey()); assertArrayEquals(pubKey, key.getPubKey()); + + key = ECKey.fromPrivate((byte[]) null); + assertNull(key); + key = ECKey.fromPrivate(new byte[0]); + assertNull(key); } @Test(expected = IllegalArgumentException.class) diff --git a/framework/src/test/java/org/tron/common/crypto/SM2KeyTest.java b/framework/src/test/java/org/tron/common/crypto/SM2KeyTest.java index b84026d2085..87e4e14698c 100644 --- a/framework/src/test/java/org/tron/common/crypto/SM2KeyTest.java +++ b/framework/src/test/java/org/tron/common/crypto/SM2KeyTest.java @@ -4,6 +4,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.tron.common.utils.client.utils.AbiUtil.generateOccupationConstantPrivateKey; @@ -64,6 +65,11 @@ public void testFromPrivateKey() { assertTrue(key.isPubKeyCanonical()); assertTrue(key.hasPrivKey()); assertArrayEquals(pubKey, key.getPubKey()); + + key = SM2.fromPrivate((byte[]) null); + assertNull(key); + key = SM2.fromPrivate(new byte[0]); + assertNull(key); } @Test(expected = IllegalArgumentException.class) diff --git a/framework/src/test/java/org/tron/core/capsule/BlockCapsuleTest.java b/framework/src/test/java/org/tron/core/capsule/BlockCapsuleTest.java index 3c86d893895..552a014a97b 100644 --- a/framework/src/test/java/org/tron/core/capsule/BlockCapsuleTest.java +++ b/framework/src/test/java/org/tron/core/capsule/BlockCapsuleTest.java @@ -134,7 +134,7 @@ public void testHasWitnessSignature() { localWitnesses = new LocalWitnesses(); localWitnesses.setPrivateKeys(Arrays.asList(privateKey)); - localWitnesses.initWitnessAccountAddress(true); + localWitnesses.initWitnessAccountAddress(null, true); Args.setLocalWitnesses(localWitnesses); Assert.assertFalse(blockCapsule0.hasWitnessSignature()); diff --git a/framework/src/test/java/org/tron/core/config/args/ArgsTest.java b/framework/src/test/java/org/tron/core/config/args/ArgsTest.java index 4b656e463be..7ad15580266 100644 --- a/framework/src/test/java/org/tron/core/config/args/ArgsTest.java +++ b/framework/src/test/java/org/tron/core/config/args/ArgsTest.java @@ -65,10 +65,10 @@ public void get() { localWitnesses = new LocalWitnesses(); localWitnesses.setPrivateKeys(Arrays.asList(privateKey)); - localWitnesses.initWitnessAccountAddress(true); + localWitnesses.initWitnessAccountAddress(null, true); Args.setLocalWitnesses(localWitnesses); address = ByteArray.toHexString(Args.getLocalWitnesses() - .getWitnessAccountAddress(CommonParameter.getInstance().isECKeyCryptoEngine())); + .getWitnessAccountAddress()); Assert.assertEquals(Constant.ADD_PRE_FIX_STRING_TESTNET, DecodeUtil.addressPreFixString); Assert.assertEquals(0, parameter.getBackupPriority()); @@ -126,7 +126,7 @@ public void get() { Assert.assertEquals(address, ByteArray.toHexString(Args.getLocalWitnesses() - .getWitnessAccountAddress(CommonParameter.getInstance().isECKeyCryptoEngine()))); + .getWitnessAccountAddress())); Assert.assertTrue(parameter.isKeystore()); } diff --git a/framework/src/test/java/org/tron/core/config/args/LocalWitnessTest.java b/framework/src/test/java/org/tron/core/config/args/LocalWitnessTest.java index 27d5effd6b1..8b9da2c7bc3 100644 --- a/framework/src/test/java/org/tron/core/config/args/LocalWitnessTest.java +++ b/framework/src/test/java/org/tron/core/config/args/LocalWitnessTest.java @@ -15,18 +15,28 @@ package org.tron.core.config.args; +import static org.junit.Assert.fail; + import com.google.common.collect.Lists; +import java.io.IOException; import org.junit.Assert; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.TemporaryFolder; import org.tron.common.utils.LocalWitnesses; import org.tron.common.utils.PublicMethod; +import org.tron.common.utils.StringUtil; +import org.tron.core.Constant; public class LocalWitnessTest { private final LocalWitnesses localWitness = new LocalWitnesses(); private static final String PRIVATE_KEY = PublicMethod.getRandomPrivateKey(); + @Rule + public final TemporaryFolder temporaryFolder = new TemporaryFolder(); + @Before public void setLocalWitness() { localWitness @@ -42,16 +52,16 @@ public void whenSetNullPrivateKey() { Assert.assertNotNull(localWitness.getPublicKey()); } - @Test + @Test(expected = IllegalArgumentException.class) public void whenSetEmptyPrivateKey() { localWitness.setPrivateKeys(Lists.newArrayList("")); - Assert.assertNotNull(localWitness.getPrivateKey()); - Assert.assertNotNull(localWitness.getPublicKey()); + fail("private key must be 64-bits hex string"); } @Test(expected = IllegalArgumentException.class) public void whenSetBadFormatPrivateKey() { localWitness.setPrivateKeys(Lists.newArrayList("a111")); + fail("private key must be 64-bits hex string"); } @Test @@ -65,6 +75,103 @@ public void whenSetPrefixPrivateKey() { Assert.assertNotNull(localWitness.getPrivateKey()); } + @Test + public void testValidPrivateKey() { + LocalWitnesses localWitnesses = new LocalWitnesses(); + + try { + localWitnesses.addPrivateKeys(PRIVATE_KEY); + Assert.assertEquals(1, localWitnesses.getPrivateKeys().size()); + Assert.assertEquals(PRIVATE_KEY, localWitnesses.getPrivateKeys().get(0)); + } catch (Exception e) { + fail(e.getMessage()); + } + } + + @Test + public void testValidPrivateKeyWithPrefix() { + LocalWitnesses localWitnesses = new LocalWitnesses(); + + try { + localWitnesses.addPrivateKeys("0x" + PRIVATE_KEY); + Assert.assertEquals(1, localWitnesses.getPrivateKeys().size()); + Assert.assertEquals("0x" + PRIVATE_KEY, localWitnesses.getPrivateKeys().get(0)); + } catch (Exception e) { + fail(e.getMessage()); + } + } + + @Test + public void testInvalidPrivateKey() { + LocalWitnesses localWitnesses = new LocalWitnesses(); + + try { + localWitnesses.addPrivateKeys(null); + fail("should throw IllegalArgumentException"); + } catch (IllegalArgumentException e) { + Assert.assertTrue(e.getMessage().contains("private key must be 64 hex string")); + } catch (Exception e) { + fail("should IllegalArgumentException,actual exception: " + e.getClass().getSimpleName()); + } + + try { + localWitnesses.addPrivateKeys(""); + fail("should throw IllegalArgumentException"); + } catch (IllegalArgumentException e) { + Assert.assertTrue(e.getMessage().contains("private key must be 64 hex string")); + } catch (Exception e) { + fail("should IllegalArgumentException,actual exception: " + e.getClass().getSimpleName()); + } + + try { + localWitnesses.addPrivateKeys(" "); + fail("should throw IllegalArgumentException"); + } catch (IllegalArgumentException e) { + Assert.assertTrue(e.getMessage().contains("private key must be 64 hex string")); + } catch (Exception e) { + fail("should IllegalArgumentException,actual exception: " + e.getClass().getSimpleName()); + } + + try { + localWitnesses.addPrivateKeys("11111"); + fail("should throw IllegalArgumentException"); + } catch (IllegalArgumentException e) { + Assert.assertTrue(e.getMessage().contains("private key must be 64 hex string")); + } catch (Exception e) { + fail("should IllegalArgumentException,actual exception: " + e.getClass().getSimpleName()); + } + + try { + String privateKey = "11111111111111111111111111111111111111111111111111111111111111 "; + localWitnesses.addPrivateKeys(privateKey); + fail("should throw IllegalArgumentException"); + } catch (IllegalArgumentException e) { + Assert.assertTrue(e.getMessage().contains("private key must be hex string")); + } catch (Exception e) { + fail("should IllegalArgumentException,actual exception: " + e.getClass().getSimpleName()); + } + + try { + String privateKey = "xy11111111111111111111111111111111111111111111111111111111111111"; + localWitnesses.addPrivateKeys(privateKey); + fail("should throw IllegalArgumentException"); + } catch (IllegalArgumentException e) { + Assert.assertTrue(e.getMessage().contains("private key must be hex string")); + } catch (Exception e) { + fail("should IllegalArgumentException,actual exception: " + e.getClass().getSimpleName()); + } + } + + @Test + public void testHexStringFormat() { + Assert.assertTrue(StringUtil.isHexadecimal("0123456789abcdefABCDEF")); + Assert.assertFalse(StringUtil.isHexadecimal(null)); + Assert.assertFalse(StringUtil.isHexadecimal("")); + Assert.assertFalse(StringUtil.isHexadecimal("abc")); + Assert.assertFalse(StringUtil.isHexadecimal(" ")); + Assert.assertFalse(StringUtil.isHexadecimal("123xyz")); + } + @Test public void getPrivateKey() { Assert.assertEquals(Lists @@ -77,14 +184,34 @@ public void testConstructor() { LocalWitnesses localWitnesses = new LocalWitnesses(PublicMethod.getRandomPrivateKey()); LocalWitnesses localWitnesses1 = new LocalWitnesses(Lists.newArrayList(PublicMethod.getRandomPrivateKey())); - localWitnesses.setWitnessAccountAddress(new byte[0]); + localWitnesses.initWitnessAccountAddress(new byte[0], true); Assert.assertNotNull(localWitnesses1.getPublicKey()); LocalWitnesses localWitnesses2 = new LocalWitnesses(); Assert.assertNull(localWitnesses2.getPrivateKey()); Assert.assertNull(localWitnesses2.getPublicKey()); - localWitnesses2.initWitnessAccountAddress(true); + localWitnesses2.initWitnessAccountAddress(null, true); LocalWitnesses localWitnesses3 = new LocalWitnesses(); - Assert.assertNotNull(localWitnesses3.getWitnessAccountAddress(true)); + Assert.assertNull(localWitnesses3.getWitnessAccountAddress()); + } + + @Test + public void testLocalWitnessConfig() throws IOException { + Args.setParam( + new String[]{"--output-directory", temporaryFolder.newFolder().toString(), "-w", "--debug"}, + "config-localtest.conf"); + LocalWitnesses witness = Args.getLocalWitnesses(); + Assert.assertNotNull(witness.getPrivateKey()); + Assert.assertNotNull(witness.getWitnessAccountAddress()); + } + + @Test + public void testNullLocalWitnessConfig() throws IOException { + Args.setParam( + new String[]{"--output-directory", temporaryFolder.newFolder().toString(), "--debug"}, + Constant.TEST_CONF); + LocalWitnesses witness = Args.getLocalWitnesses(); + Assert.assertNull(witness.getPrivateKey()); + Assert.assertNull(witness.getWitnessAccountAddress()); } } diff --git a/framework/src/test/java/org/tron/core/config/args/WitnessInitializerTest.java b/framework/src/test/java/org/tron/core/config/args/WitnessInitializerTest.java new file mode 100644 index 00000000000..7364b1f9b3a --- /dev/null +++ b/framework/src/test/java/org/tron/core/config/args/WitnessInitializerTest.java @@ -0,0 +1,268 @@ +package org.tron.core.config.args; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import com.typesafe.config.Config; +import com.typesafe.config.ConfigFactory; +import java.io.File; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import org.bouncycastle.util.encoders.Hex; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.MockedStatic; +import org.tron.common.crypto.SignInterface; +import org.tron.common.parameter.CommonParameter; +import org.tron.common.utils.ByteArray; +import org.tron.common.utils.LocalWitnesses; +import org.tron.common.utils.PublicMethod; +import org.tron.common.utils.client.utils.Base58; +import org.tron.core.Constant; +import org.tron.core.exception.TronError; +import org.tron.core.exception.TronError.ErrCode; +import org.tron.keystore.Credentials; +import org.tron.keystore.WalletUtils; + +public class WitnessInitializerTest { + + private Config config; + private WitnessInitializer witnessInitializer; + + private static final String privateKey = PublicMethod.getRandomPrivateKey(); + private static final String address = Base58.encode58Check( + ByteArray.fromHexString(PublicMethod.getHexAddressByPrivateKey(privateKey))); + private static final String invalidAddress = "RJCzdnv88Hvqa2jB1C9dMmMYHr5DFdF2R3"; + + @Before + public void setUp() { + config = ConfigFactory.empty(); + witnessInitializer = new WitnessInitializer(config); + } + + @After + public void clear() { + Args.clearParam(); + } + + @Test + public void testInitLocalWitnessesEmpty() { + Args.PARAMETER.setWitness(false); + + LocalWitnesses result = witnessInitializer.initLocalWitnesses(); + assertNotNull(result); + assertTrue(result.getPrivateKeys().isEmpty()); + + Args.PARAMETER.setWitness(true); + LocalWitnesses localWitnesses = witnessInitializer.initLocalWitnesses(); + assertTrue(localWitnesses.getPrivateKeys().isEmpty()); + + String configString = "localwitness = [] \n localwitnesskeystore = []"; + config = ConfigFactory.parseString(configString); + witnessInitializer = new WitnessInitializer(config); + localWitnesses = witnessInitializer.initLocalWitnesses(); + assertTrue(localWitnesses.getPrivateKeys().isEmpty()); + } + + @Test + public void testTryInitFromCommandLine() + throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, + InvocationTargetException { + Field privateKeyField = CommonParameter.class.getDeclaredField("privateKey"); + privateKeyField.setAccessible(true); + privateKeyField.set(Args.getInstance(), ""); + + witnessInitializer = new WitnessInitializer(config); + Method method = WitnessInitializer.class.getDeclaredMethod( + "tryInitFromCommandLine"); + method.setAccessible(true); + boolean result = (boolean) method.invoke(witnessInitializer); + assertFalse(result); + + privateKeyField.set(Args.getInstance(), privateKey); + method.invoke(witnessInitializer); + result = (boolean) method.invoke(witnessInitializer); + assertTrue(result); + + Field witnessAddress = CommonParameter.class.getDeclaredField("witnessAddress"); + witnessAddress.setAccessible(true); + witnessAddress.set(Args.getInstance(), address); + result = (boolean) method.invoke(witnessInitializer); + assertTrue(result); + + witnessAddress.set(Args.getInstance(), invalidAddress); + InvocationTargetException thrown = assertThrows(InvocationTargetException.class, + () -> method.invoke(witnessInitializer)); + TronError targetException = (TronError) thrown.getTargetException(); + assertEquals(ErrCode.WITNESS_INIT, targetException.getErrCode()); + } + + @Test + public void testTryInitFromConfig() + throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + witnessInitializer = new WitnessInitializer(config); + Method method = WitnessInitializer.class.getDeclaredMethod( + "tryInitFromConfig"); + method.setAccessible(true); + boolean result = (boolean) method.invoke(witnessInitializer); + assertFalse(result); + + String configString = "localwitness = []"; + config = ConfigFactory.parseString(configString); + witnessInitializer = new WitnessInitializer(config); + result = (boolean) method.invoke(witnessInitializer); + assertFalse(result); + + configString = "localwitness = [" + privateKey + "]"; + config = ConfigFactory.parseString(configString); + witnessInitializer = new WitnessInitializer(config); + result = (boolean) method.invoke(witnessInitializer); + assertTrue(result); + + configString = "localWitnessAccountAddress = " + address + "\n" + + "localwitness = [\n" + privateKey + "]"; + config = ConfigFactory.parseString(configString); + witnessInitializer = new WitnessInitializer(config); + result = (boolean) method.invoke(witnessInitializer); + assertTrue(result); + + configString = "localwitness = [\n" + privateKey + "\n" + privateKey + "]"; + config = ConfigFactory.parseString(configString); + witnessInitializer = new WitnessInitializer(config); + result = (boolean) method.invoke(witnessInitializer); + assertTrue(result); + + configString = "localWitnessAccountAddress = " + invalidAddress + "\n" + + "localwitness = [\n" + privateKey + "]"; + config = ConfigFactory.parseString(configString); + witnessInitializer = new WitnessInitializer(config); + InvocationTargetException thrown = assertThrows(InvocationTargetException.class, + () -> method.invoke(witnessInitializer)); + TronError targetException = (TronError) thrown.getTargetException(); + assertEquals(ErrCode.WITNESS_INIT, targetException.getErrCode()); + + configString = "localWitnessAccountAddress = " + address + "\n" + + "localwitness = [\n" + privateKey + "\n" + privateKey + "]"; + config = ConfigFactory.parseString(configString); + witnessInitializer = new WitnessInitializer(config); + thrown = assertThrows(InvocationTargetException.class, + () -> method.invoke(witnessInitializer)); + targetException = (TronError) thrown.getTargetException(); + assertEquals(ErrCode.WITNESS_INIT, targetException.getErrCode()); + } + + @Test + public void testTryInitFromKeystore() + throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, + NoSuchFieldException { + witnessInitializer = new WitnessInitializer(config); + Method method = WitnessInitializer.class.getDeclaredMethod( + "tryInitFromKeystore"); + method.setAccessible(true); + method.invoke(witnessInitializer); + Field localWitnessField = WitnessInitializer.class.getDeclaredField("localWitnesses"); + localWitnessField.setAccessible(true); + LocalWitnesses localWitnesses = (LocalWitnesses) localWitnessField.get(witnessInitializer); + assertTrue(localWitnesses.getPrivateKeys().isEmpty()); + + String configString = "localwitnesskeystore = []"; + Config emptyListConfig = ConfigFactory.parseString(configString); + witnessInitializer = new WitnessInitializer(emptyListConfig); + method.invoke(witnessInitializer); + localWitnesses = (LocalWitnesses) localWitnessField.get(witnessInitializer); + assertTrue(localWitnesses.getPrivateKeys().isEmpty()); + } + + @Test + public void testTryInitFromKeyStore2() + throws NoSuchFieldException, IllegalAccessException { + Args.PARAMETER.setWitness(true); + Config mockConfig = mock(Config.class); + when(mockConfig.hasPath(Constant.LOCAL_WITNESS_KEYSTORE)).thenReturn(false); + witnessInitializer = new WitnessInitializer(mockConfig); + witnessInitializer.initLocalWitnesses(); + verify(mockConfig, never()).getStringList(anyString()); + + when(mockConfig.hasPath(Constant.LOCAL_WITNESS_KEYSTORE)).thenReturn(true); + when(mockConfig.getStringList(Constant.LOCAL_WITNESS_KEYSTORE)).thenReturn(new ArrayList<>()); + witnessInitializer = new WitnessInitializer(mockConfig); + witnessInitializer.initLocalWitnesses(); + verify(mockConfig, times(1)).getStringList(Constant.LOCAL_WITNESS_KEYSTORE); + + List keystores = new ArrayList<>(); + keystores.add("keystore1.json"); + keystores.add("keystore2.json"); + when(mockConfig.hasPath(Constant.LOCAL_WITNESS_KEYSTORE)).thenReturn(true); + when(mockConfig.getStringList(Constant.LOCAL_WITNESS_KEYSTORE)).thenReturn(keystores); + + Field password = CommonParameter.class.getDeclaredField("password"); + password.setAccessible(true); + password.set(Args.getInstance(), "password"); + + try (MockedStatic mockedWalletUtils = mockStatic(WalletUtils.class); + MockedStatic mockedByteArray = mockStatic(ByteArray.class)) { + // Mock WalletUtils.loadCredentials + Credentials credentials = mock(Credentials.class); + SignInterface signInterface = mock(SignInterface.class); + when(credentials.getSignInterface()).thenReturn(signInterface); + byte[] keyBytes = Hex.decode(privateKey); + when(signInterface.getPrivateKey()).thenReturn(keyBytes); + mockedWalletUtils.when(() -> WalletUtils.loadCredentials(anyString(), any(File.class))) + .thenReturn(credentials); + mockedByteArray.when(() -> ByteArray.toHexString(any())).thenReturn(privateKey); + mockedByteArray.when(() -> ByteArray.fromHexString(anyString())).thenReturn(keyBytes); + + witnessInitializer = new WitnessInitializer(mockConfig); + Field localWitnessField = WitnessInitializer.class.getDeclaredField("localWitnesses"); + localWitnessField.setAccessible(true); + localWitnessField.set(witnessInitializer, new LocalWitnesses(privateKey)); + LocalWitnesses localWitnesses = witnessInitializer.initLocalWitnesses(); + assertFalse(localWitnesses.getPrivateKeys().isEmpty()); + } + } + + @Test + public void testGetWitnessAddress() + throws InvocationTargetException, IllegalAccessException, NoSuchMethodException, + NoSuchFieldException { + witnessInitializer = new WitnessInitializer(config); + Method method = WitnessInitializer.class.getDeclaredMethod( + "getWitnessAddress"); + method.setAccessible(true); + byte[] result = (byte[]) method.invoke(witnessInitializer); + assertNull(result); + + String configString = "localWitnessAccountAddress = " + address; + config = ConfigFactory.parseString(configString); + witnessInitializer = new WitnessInitializer(config); + Field localWitnessField = WitnessInitializer.class.getDeclaredField("localWitnesses"); + localWitnessField.setAccessible(true); + localWitnessField.set(witnessInitializer, new LocalWitnesses(privateKey)); + result = (byte[]) method.invoke(witnessInitializer); + assertNotNull(result); + + configString = "localWitnessAccountAddress = " + invalidAddress; + config = ConfigFactory.parseString(configString); + witnessInitializer = new WitnessInitializer(config); + InvocationTargetException thrown = assertThrows(InvocationTargetException.class, + () -> method.invoke(witnessInitializer)); + TronError targetException = (TronError) thrown.getTargetException(); + assertEquals(ErrCode.WITNESS_INIT, targetException.getErrCode()); + } +} diff --git a/framework/src/test/java/org/tron/core/db/ManagerTest.java b/framework/src/test/java/org/tron/core/db/ManagerTest.java index db219377b74..d05334c5b94 100755 --- a/framework/src/test/java/org/tron/core/db/ManagerTest.java +++ b/framework/src/test/java/org/tron/core/db/ManagerTest.java @@ -136,7 +136,7 @@ public void init() throws IOException { localWitnesses = new LocalWitnesses(); localWitnesses.setPrivateKeys(Arrays.asList(privateKey)); - localWitnesses.initWitnessAccountAddress(true); + localWitnesses.initWitnessAccountAddress(null, true); Args.setLocalWitnesses(localWitnesses); blockCapsule2 = diff --git a/framework/src/test/java/org/tron/core/db/TransactionExpireTest.java b/framework/src/test/java/org/tron/core/db/TransactionExpireTest.java index 420b54525e4..f563203b71a 100644 --- a/framework/src/test/java/org/tron/core/db/TransactionExpireTest.java +++ b/framework/src/test/java/org/tron/core/db/TransactionExpireTest.java @@ -57,7 +57,7 @@ private void initLocalWitness() { String randomPrivateKey = PublicMethod.getRandomPrivateKey(); LocalWitnesses localWitnesses = new LocalWitnesses(); localWitnesses.setPrivateKeys(Arrays.asList(randomPrivateKey)); - localWitnesses.initWitnessAccountAddress(true); + localWitnesses.initWitnessAccountAddress(null, true); Args.setLocalWitnesses(localWitnesses); } @@ -85,7 +85,7 @@ public void testExpireTransaction() { TransferContract transferContract = TransferContract.newBuilder() .setAmount(1L) .setOwnerAddress(ByteString.copyFrom(Args.getLocalWitnesses() - .getWitnessAccountAddress(CommonParameter.getInstance().isECKeyCryptoEngine()))) + .getWitnessAccountAddress())) .setToAddress(ByteString.copyFrom(ByteArray.fromHexString( (Wallet.getAddressPreFixString() + "A389132D6639FBDA4FBC8B659264E6B7C90DB086")))) .build(); @@ -116,8 +116,7 @@ public void testExpireTransactionNew() { .saveLatestBlockHeaderTimestamp(blockCapsule.getTimeStamp()); dbManager.updateRecentBlock(blockCapsule); initLocalWitness(); - byte[] address = Args.getLocalWitnesses() - .getWitnessAccountAddress(CommonParameter.getInstance().isECKeyCryptoEngine()); + byte[] address = Args.getLocalWitnesses().getWitnessAccountAddress(); ByteString addressByte = ByteString.copyFrom(address); AccountCapsule accountCapsule = new AccountCapsule(Protocol.Account.newBuilder().setAddress(addressByte).build()); @@ -157,8 +156,7 @@ public void testTransactionApprovedList() { dbManager.updateRecentBlock(blockCapsule); initLocalWitness(); - byte[] address = Args.getLocalWitnesses() - .getWitnessAccountAddress(CommonParameter.getInstance().isECKeyCryptoEngine()); + byte[] address = Args.getLocalWitnesses().getWitnessAccountAddress(); TransferContract transferContract = TransferContract.newBuilder() .setAmount(1L) .setOwnerAddress(ByteString.copyFrom(address)) diff --git a/framework/src/test/java/org/tron/core/net/services/RelayServiceTest.java b/framework/src/test/java/org/tron/core/net/services/RelayServiceTest.java index 2ed09019fc9..62ccd34eb9e 100644 --- a/framework/src/test/java/org/tron/core/net/services/RelayServiceTest.java +++ b/framework/src/test/java/org/tron/core/net/services/RelayServiceTest.java @@ -6,6 +6,7 @@ import com.google.protobuf.ByteString; import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.InetSocketAddress; import java.util.ArrayList; @@ -219,4 +220,32 @@ private void testCheckHelloMessage() { assert false; } } -} \ No newline at end of file + + @Test + public void testNullWitnessAddress() { + try { + Class clazz = service.getClass(); + + Field keySizeField = clazz.getDeclaredField("keySize"); + keySizeField.setAccessible(true); + keySizeField.set(service, 0); + + Field witnessAddressField = clazz.getDeclaredField("witnessAddress"); + witnessAddressField.setAccessible(true); + witnessAddressField.set(service, null); + + Method isActiveWitnessMethod = clazz.getDeclaredMethod("isActiveWitness"); + isActiveWitnessMethod.setAccessible(true); + + Boolean result = (Boolean) isActiveWitnessMethod.invoke(service); + Assert.assertNotEquals(Boolean.TRUE, result); + + witnessAddressField.set(service, ByteString.copyFrom(new byte[21])); + result = (Boolean) isActiveWitnessMethod.invoke(service); + Assert.assertNotEquals(Boolean.TRUE, result); + } catch (NoSuchMethodException | NoSuchFieldException + | IllegalAccessException | InvocationTargetException e) { + Assert.fail("Reflection invocation failed: " + e.getMessage()); + } + } +} diff --git a/framework/src/test/java/org/tron/core/zksnark/ShieldedReceiveTest.java b/framework/src/test/java/org/tron/core/zksnark/ShieldedReceiveTest.java index 4012029e8ae..013d58b63ca 100755 --- a/framework/src/test/java/org/tron/core/zksnark/ShieldedReceiveTest.java +++ b/framework/src/test/java/org/tron/core/zksnark/ShieldedReceiveTest.java @@ -128,7 +128,7 @@ public class ShieldedReceiveTest extends BaseTest { private static boolean init; static { - Args.setParam(new String[]{"--output-directory", dbPath()}, "config-localtest.conf"); + Args.setParam(new String[]{"--output-directory", dbPath(), "-w"}, "config-localtest.conf"); ADDRESS_ONE_PRIVATE_KEY = getRandomPrivateKey(); FROM_ADDRESS = getHexAddressByPrivateKey(ADDRESS_ONE_PRIVATE_KEY); } diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index 53d4958f1e5..e8f2596e195 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -1569,12 +1569,12 @@ - - - + + + - - + + diff --git a/plugins/build.gradle b/plugins/build.gradle index 40d68e19d2f..e03e9a7c49a 100644 --- a/plugins/build.gradle +++ b/plugins/build.gradle @@ -37,7 +37,7 @@ dependencies { implementation group: 'info.picocli', name: 'picocli', version: '4.6.3' implementation group: 'com.typesafe', name: 'config', version: '1.3.2' implementation group: 'me.tongfei', name: 'progressbar', version: '0.9.3' - implementation group: 'org.bouncycastle', name: 'bcprov-jdk15on', version: '1.69' + implementation group: 'org.bouncycastle', name: 'bcprov-jdk18on', version: '1.79' if (rootProject.archInfo.isArm64) { testRuntimeOnly group: 'org.fusesource.hawtjni', name: 'hawtjni-runtime', version: '1.18' // for test implementation project(":platform") @@ -186,4 +186,4 @@ task copyToParent(type: Copy) { -build.finalizedBy(copyToParent) \ No newline at end of file +build.finalizedBy(copyToParent) From cfaf4c02ea9d99b70a59e4c11a1263d2a49365d2 Mon Sep 17 00:00:00 2001 From: Edward Date: Wed, 20 Aug 2025 15:06:22 +0800 Subject: [PATCH 32/59] feat(API): return witness list with latest vote count in desc order (#6373) * add wallet draft logic * update logic * remove unused logic * revert merge import folders * fix merge import * fix review * fix format * sort witness using the same logic as in maintence * add rpc, http test * update comments * delete unused code * fix review * fix style * fix style * fix check style * add check maintenance period and throw error * add test case for throw error * fix review * update comments * fix build * fix checkstyle * fix style --- .../org/tron/core/store/WitnessStore.java | 2 +- .../java/org/tron/core/config/Parameter.java | 1 + .../MaintenanceUnavailableException.java | 20 +++ .../src/main/java/org/tron/core/Wallet.java | 108 ++++++++++++++ .../org/tron/core/services/RpcApiService.java | 25 ++++ .../services/http/FullNodeHttpApiService.java | 6 + .../GetPaginatedNowWitnessListServlet.java | 52 +++++++ .../RpcApiServiceOnSolidity.java | 7 + ...inatedNowWitnessListOnSolidityServlet.java | 24 +++ .../solidity/HttpApiOnSolidityService.java | 8 +- .../test/java/org/tron/core/WalletTest.java | 138 ++++++++++++++++++ .../org/tron/core/db/WitnessStoreTest.java | 4 +- .../core/services/RpcApiServicesTest.java | 8 + .../core/services/http/HttpServletTest.java | 4 + protocol/src/main/protos/api/api.proto | 6 + 15 files changed, 407 insertions(+), 6 deletions(-) create mode 100644 common/src/main/java/org/tron/core/exception/MaintenanceUnavailableException.java create mode 100644 framework/src/main/java/org/tron/core/services/http/GetPaginatedNowWitnessListServlet.java create mode 100644 framework/src/main/java/org/tron/core/services/interfaceOnSolidity/http/GetPaginatedNowWitnessListOnSolidityServlet.java diff --git a/chainbase/src/main/java/org/tron/core/store/WitnessStore.java b/chainbase/src/main/java/org/tron/core/store/WitnessStore.java index 9f444d3333d..9c30df195af 100644 --- a/chainbase/src/main/java/org/tron/core/store/WitnessStore.java +++ b/chainbase/src/main/java/org/tron/core/store/WitnessStore.java @@ -55,7 +55,7 @@ public List getWitnessStandby(boolean isSortOpt) { return ret; } - public void sortWitnesses(List witnesses, boolean isSortOpt) { + public static void sortWitnesses(List witnesses, boolean isSortOpt) { witnesses.sort(Comparator.comparingLong(WitnessCapsule::getVoteCount).reversed() .thenComparing(isSortOpt ? Comparator.comparing(WitnessCapsule::createReadableString).reversed() diff --git a/common/src/main/java/org/tron/core/config/Parameter.java b/common/src/main/java/org/tron/core/config/Parameter.java index 7d0681a9f91..8a86d67fe4c 100644 --- a/common/src/main/java/org/tron/core/config/Parameter.java +++ b/common/src/main/java/org/tron/core/config/Parameter.java @@ -108,6 +108,7 @@ public class DatabaseConstants { public static final int PROPOSAL_COUNT_LIMIT_MAX = 1000; public static final int EXCHANGE_COUNT_LIMIT_MAX = 1000; public static final int MARKET_COUNT_LIMIT_MAX = 1000; + public static final int WITNESS_COUNT_LIMIT_MAX = 1000; } public class AdaptiveResourceLimitConstants { diff --git a/common/src/main/java/org/tron/core/exception/MaintenanceUnavailableException.java b/common/src/main/java/org/tron/core/exception/MaintenanceUnavailableException.java new file mode 100644 index 00000000000..1911f84a616 --- /dev/null +++ b/common/src/main/java/org/tron/core/exception/MaintenanceUnavailableException.java @@ -0,0 +1,20 @@ +package org.tron.core.exception; + +/** + * Maintenance unavailable exception - thrown when service is in maintenance state + * Please try again later + */ +public class MaintenanceUnavailableException extends TronException { + + public MaintenanceUnavailableException() { + super(); + } + + public MaintenanceUnavailableException(String message) { + super(message); + } + + public MaintenanceUnavailableException(String message, Throwable cause) { + super(message, cause); + } +} \ No newline at end of file diff --git a/framework/src/main/java/org/tron/core/Wallet.java b/framework/src/main/java/org/tron/core/Wallet.java index 125443d59dd..b433352b471 100755 --- a/framework/src/main/java/org/tron/core/Wallet.java +++ b/framework/src/main/java/org/tron/core/Wallet.java @@ -30,6 +30,7 @@ import static org.tron.core.config.Parameter.DatabaseConstants.EXCHANGE_COUNT_LIMIT_MAX; import static org.tron.core.config.Parameter.DatabaseConstants.MARKET_COUNT_LIMIT_MAX; import static org.tron.core.config.Parameter.DatabaseConstants.PROPOSAL_COUNT_LIMIT_MAX; +import static org.tron.core.config.Parameter.DatabaseConstants.WITNESS_COUNT_LIMIT_MAX; import static org.tron.core.services.jsonrpc.JsonRpcApiUtil.parseEnergyFee; import static org.tron.core.services.jsonrpc.TronJsonRpcImpl.EARLIEST_STR; import static org.tron.core.services.jsonrpc.TronJsonRpcImpl.FINALIZED_STR; @@ -44,6 +45,7 @@ import com.google.common.collect.DiscreteDomain; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; +import com.google.common.collect.Maps; import com.google.common.collect.Range; import com.google.protobuf.ByteString; import com.google.protobuf.InvalidProtocolBufferException; @@ -59,6 +61,7 @@ import java.util.Map.Entry; import java.util.Objects; import java.util.Optional; +import java.util.stream.Collectors; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.ArrayUtils; @@ -161,6 +164,7 @@ import org.tron.core.capsule.TransactionInfoCapsule; import org.tron.core.capsule.TransactionResultCapsule; import org.tron.core.capsule.TransactionRetCapsule; +import org.tron.core.capsule.VotesCapsule; import org.tron.core.capsule.WitnessCapsule; import org.tron.core.capsule.utils.MarketUtils; import org.tron.core.config.args.Args; @@ -177,6 +181,7 @@ import org.tron.core.exception.DupTransactionException; import org.tron.core.exception.HeaderNotFound; import org.tron.core.exception.ItemNotFoundException; +import org.tron.core.exception.MaintenanceUnavailableException; import org.tron.core.exception.NonUniqueObjectException; import org.tron.core.exception.PermissionException; import org.tron.core.exception.SignatureFormatException; @@ -201,6 +206,8 @@ import org.tron.core.store.MarketPairPriceToOrderStore; import org.tron.core.store.MarketPairToPriceStore; import org.tron.core.store.StoreFactory; +import org.tron.core.store.VotesStore; +import org.tron.core.store.WitnessStore; import org.tron.core.utils.TransactionUtil; import org.tron.core.vm.program.Program; import org.tron.core.zen.ShieldedTRC20ParametersBuilder; @@ -764,6 +771,107 @@ public WitnessList getWitnessList() { return builder.build(); } + public WitnessList getPaginatedNowWitnessList(long offset, long limit) throws + MaintenanceUnavailableException { + if (limit <= 0 || offset < 0) { + return null; + } + if (limit > WITNESS_COUNT_LIMIT_MAX) { + limit = WITNESS_COUNT_LIMIT_MAX; + } + + /* + In the maintenance period, the VoteStores will be cleared. + To avoid the race condition of VoteStores deleted but Witness vote counts not updated, + return retry error. + */ + if (chainBaseManager.getDynamicPropertiesStore().getStateFlag() == 1) { + String message = + "Service temporarily unavailable during maintenance period. Please try again later."; + throw new MaintenanceUnavailableException(message); + } + // It contains the final vote count at the end of the last epoch. + List witnessCapsuleList = chainBaseManager.getWitnessStore().getAllWitnesses(); + if (offset >= witnessCapsuleList.size()) { + return null; + } + + VotesStore votesStore = chainBaseManager.getVotesStore(); + // Count the vote changes for each witness in the current epoch, it is maybe negative. + Map countWitness = countVote(votesStore); + + // Iterate through the witness list to apply vote changes and calculate the real-time vote count + witnessCapsuleList.forEach(witnessCapsule -> { + long voteCount = countWitness.getOrDefault(witnessCapsule.getAddress(), 0L); + witnessCapsule.setVoteCount(witnessCapsule.getVoteCount() + voteCount); + }); + + // Use the same sorting logic as in the Maintenance period + WitnessStore.sortWitnesses(witnessCapsuleList, + chainBaseManager.getDynamicPropertiesStore().allowWitnessSortOptimization()); + + List sortedWitnessList = witnessCapsuleList.stream() + .skip(offset) + .limit(limit) + .collect(Collectors.toList()); + + WitnessList.Builder builder = WitnessList.newBuilder(); + sortedWitnessList.forEach(witnessCapsule -> + builder.addWitnesses(witnessCapsule.getInstance())); + + return builder.build(); + } + + /** + * Counts vote changes for witnesses in the current epoch. + * + * Vote count changes are tracked as follows: + * - Negative values for votes removed from previous witness in the last epoch + * - Positive values for votes added to new witness in the current epoch + * + * Example: + * an Account X had 100 votes for witness W1 in the previous epoch. + * In the current epoch, X changes votes to: + * - W2: 60 votes + * - W3: 80 votes + * + * Resulting vote changes: + * - W1: -100 (votes removed) + * - W2: +60 (new votes) + * - W3: +80 (new votes) + */ + private Map countVote(VotesStore votesStore) { + // Initialize a result map to store vote changes for each witness + Map countWitness = Maps.newHashMap(); + + // VotesStore is a key-value store, where the key is the address of the voter + Iterator> dbIterator = votesStore.iterator(); + + while (dbIterator.hasNext()) { + Entry next = dbIterator.next(); + VotesCapsule votes = next.getValue(); + + /** + * VotesCapsule contains two lists: + * - Old votes: Last votes from the previous epoch, updated in maintenance period + * - New votes: Latest votes in current epoch, updated after each vote transaction + */ + votes.getOldVotes().forEach(vote -> { + ByteString voteAddress = vote.getVoteAddress(); + long voteCount = vote.getVoteCount(); + countWitness.put(voteAddress, + countWitness.getOrDefault(voteAddress, 0L) - voteCount); + }); + votes.getNewVotes().forEach(vote -> { + ByteString voteAddress = vote.getVoteAddress(); + long voteCount = vote.getVoteCount(); + countWitness.put(voteAddress, + countWitness.getOrDefault(voteAddress, 0L) + voteCount); + }); + } + return countWitness; + } + public ProposalList getProposalList() { ProposalList.Builder builder = ProposalList.newBuilder(); List proposalCapsuleList = diff --git a/framework/src/main/java/org/tron/core/services/RpcApiService.java b/framework/src/main/java/org/tron/core/services/RpcApiService.java index b443d470b0a..63e7ba03fc7 100755 --- a/framework/src/main/java/org/tron/core/services/RpcApiService.java +++ b/framework/src/main/java/org/tron/core/services/RpcApiService.java @@ -89,6 +89,7 @@ import org.tron.core.exception.ContractExeException; import org.tron.core.exception.ContractValidateException; import org.tron.core.exception.ItemNotFoundException; +import org.tron.core.exception.MaintenanceUnavailableException; import org.tron.core.exception.NonUniqueObjectException; import org.tron.core.exception.StoreException; import org.tron.core.exception.VMIllegalException; @@ -382,6 +383,18 @@ public void listWitnesses(EmptyMessage request, StreamObserver resp responseObserver.onCompleted(); } + @Override + public void getPaginatedNowWitnessList(PaginatedMessage request, + StreamObserver responseObserver) { + try { + responseObserver.onNext( + wallet.getPaginatedNowWitnessList(request.getOffset(), request.getLimit())); + } catch (MaintenanceUnavailableException e) { + responseObserver.onError(getRunTimeException(e)); + } + responseObserver.onCompleted(); + } + @Override public void getAssetIssueList(EmptyMessage request, StreamObserver responseObserver) { @@ -1845,6 +1858,18 @@ public void listWitnesses(EmptyMessage request, responseObserver.onCompleted(); } + @Override + public void getPaginatedNowWitnessList(PaginatedMessage request, + StreamObserver responseObserver) { + try { + responseObserver.onNext( + wallet.getPaginatedNowWitnessList(request.getOffset(), request.getLimit())); + } catch (MaintenanceUnavailableException e) { + responseObserver.onError(getRunTimeException(e)); + } + responseObserver.onCompleted(); + } + @Override public void listProposals(EmptyMessage request, StreamObserver responseObserver) { diff --git a/framework/src/main/java/org/tron/core/services/http/FullNodeHttpApiService.java b/framework/src/main/java/org/tron/core/services/http/FullNodeHttpApiService.java index 76785218096..3ad4ace62fc 100644 --- a/framework/src/main/java/org/tron/core/services/http/FullNodeHttpApiService.java +++ b/framework/src/main/java/org/tron/core/services/http/FullNodeHttpApiService.java @@ -86,6 +86,8 @@ public class FullNodeHttpApiService extends HttpService { @Autowired private ListWitnessesServlet listWitnessesServlet; @Autowired + private GetPaginatedNowWitnessListServlet getPaginatedNowWitnessListServlet; + @Autowired private GetAssetIssueListServlet getAssetIssueListServlet; @Autowired private GetPaginatedAssetIssueListServlet getPaginatedAssetIssueListServlet; @@ -342,7 +344,11 @@ protected void addServlet(ServletContextHandler context) { context.addServlet( new ServletHolder(getTransactionCountByBlockNumServlet), "/wallet/gettransactioncountbyblocknum"); + // Get the list of witnesses info with contains vote counts for last epoch/maintenance context.addServlet(new ServletHolder(listWitnessesServlet), "/wallet/listwitnesses"); + // Get the paged list of witnesses info with realtime vote counts + context.addServlet(new ServletHolder(getPaginatedNowWitnessListServlet), + "/wallet/getpaginatednowwitnesslist"); context.addServlet(new ServletHolder(getAssetIssueListServlet), "/wallet/getassetissuelist"); context.addServlet( new ServletHolder(getPaginatedAssetIssueListServlet), diff --git a/framework/src/main/java/org/tron/core/services/http/GetPaginatedNowWitnessListServlet.java b/framework/src/main/java/org/tron/core/services/http/GetPaginatedNowWitnessListServlet.java new file mode 100644 index 00000000000..e53ab6610ec --- /dev/null +++ b/framework/src/main/java/org/tron/core/services/http/GetPaginatedNowWitnessListServlet.java @@ -0,0 +1,52 @@ +package org.tron.core.services.http; + +import java.io.IOException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.tron.api.GrpcAPI; +import org.tron.core.Wallet; +import org.tron.core.exception.MaintenanceUnavailableException; + +// Get the paged list of witnesses info with realtime vote counts +@Component +@Slf4j(topic = "API") +public class GetPaginatedNowWitnessListServlet extends RateLimiterServlet { + + @Autowired + private Wallet wallet; + + protected void doGet(HttpServletRequest request, HttpServletResponse response) { + try { + boolean visible = Util.getVisible(request); + long offset = Long.parseLong(request.getParameter("offset")); + long limit = Long.parseLong(request.getParameter("limit")); + fillResponse(offset, limit, visible, response); + } catch (Exception e) { + Util.processError(e, response); + } + } + + protected void doPost(HttpServletRequest request, HttpServletResponse response) { + try { + PostParams params = PostParams.getPostParams(request); + GrpcAPI.PaginatedMessage.Builder build = GrpcAPI.PaginatedMessage.newBuilder(); + JsonFormat.merge(params.getParams(), build, params.isVisible()); + fillResponse(build.getOffset(), build.getLimit(), params.isVisible(), response); + } catch (Exception e) { + Util.processError(e, response); + } + } + + private void fillResponse(long offset, long limit, boolean visible, HttpServletResponse response) + throws IOException, MaintenanceUnavailableException { + GrpcAPI.WitnessList reply = wallet.getPaginatedNowWitnessList(offset, limit); + if (reply != null) { + response.getWriter().println(JsonFormat.printToString(reply, visible)); + } else { + response.getWriter().println("{}"); + } + } +} diff --git a/framework/src/main/java/org/tron/core/services/interfaceOnSolidity/RpcApiServiceOnSolidity.java b/framework/src/main/java/org/tron/core/services/interfaceOnSolidity/RpcApiServiceOnSolidity.java index aa566f56042..315d70df8d6 100755 --- a/framework/src/main/java/org/tron/core/services/interfaceOnSolidity/RpcApiServiceOnSolidity.java +++ b/framework/src/main/java/org/tron/core/services/interfaceOnSolidity/RpcApiServiceOnSolidity.java @@ -162,6 +162,13 @@ public void listWitnesses(EmptyMessage request, StreamObserver resp () -> rpcApiService.getWalletSolidityApi().listWitnesses(request, responseObserver)); } + public void getPaginatedNowWitnessList(PaginatedMessage request, + StreamObserver responseObserver) { + walletOnSolidity.futureGet( + () -> rpcApiService.getWalletSolidityApi() + .getPaginatedNowWitnessList(request, responseObserver)); + } + @Override public void getAssetIssueById(BytesMessage request, StreamObserver responseObserver) { diff --git a/framework/src/main/java/org/tron/core/services/interfaceOnSolidity/http/GetPaginatedNowWitnessListOnSolidityServlet.java b/framework/src/main/java/org/tron/core/services/interfaceOnSolidity/http/GetPaginatedNowWitnessListOnSolidityServlet.java new file mode 100644 index 00000000000..4578393ec76 --- /dev/null +++ b/framework/src/main/java/org/tron/core/services/interfaceOnSolidity/http/GetPaginatedNowWitnessListOnSolidityServlet.java @@ -0,0 +1,24 @@ +package org.tron.core.services.interfaceOnSolidity.http; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.tron.core.services.http.GetPaginatedNowWitnessListServlet; +import org.tron.core.services.interfaceOnSolidity.WalletOnSolidity; + +@Component +@Slf4j(topic = "API") +public class GetPaginatedNowWitnessListOnSolidityServlet extends GetPaginatedNowWitnessListServlet { + @Autowired + private WalletOnSolidity walletOnSolidity; + + protected void doGet(HttpServletRequest request, HttpServletResponse response) { + walletOnSolidity.futureGet(() -> super.doGet(request, response)); + } + + protected void doPost(HttpServletRequest request, HttpServletResponse response) { + walletOnSolidity.futureGet(() -> super.doPost(request, response)); + } +} diff --git a/framework/src/main/java/org/tron/core/services/interfaceOnSolidity/http/solidity/HttpApiOnSolidityService.java b/framework/src/main/java/org/tron/core/services/interfaceOnSolidity/http/solidity/HttpApiOnSolidityService.java index b1d940ce2cd..f69597959f8 100644 --- a/framework/src/main/java/org/tron/core/services/interfaceOnSolidity/http/solidity/HttpApiOnSolidityService.java +++ b/framework/src/main/java/org/tron/core/services/interfaceOnSolidity/http/solidity/HttpApiOnSolidityService.java @@ -3,8 +3,6 @@ import java.util.EnumSet; import javax.servlet.DispatcherType; import lombok.extern.slf4j.Slf4j; -import org.eclipse.jetty.server.ConnectionLimit; -import org.eclipse.jetty.server.Server; import org.eclipse.jetty.servlet.FilterHolder; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; @@ -46,6 +44,7 @@ import org.tron.core.services.interfaceOnSolidity.http.GetNodeInfoOnSolidityServlet; import org.tron.core.services.interfaceOnSolidity.http.GetNowBlockOnSolidityServlet; import org.tron.core.services.interfaceOnSolidity.http.GetPaginatedAssetIssueListOnSolidityServlet; +import org.tron.core.services.interfaceOnSolidity.http.GetPaginatedNowWitnessListOnSolidityServlet; import org.tron.core.services.interfaceOnSolidity.http.GetRewardOnSolidityServlet; import org.tron.core.services.interfaceOnSolidity.http.GetTransactionCountByBlockNumOnSolidityServlet; import org.tron.core.services.interfaceOnSolidity.http.GetTransactionInfoByBlockNumOnSolidityServlet; @@ -60,7 +59,6 @@ import org.tron.core.services.interfaceOnSolidity.http.ScanShieldedTRC20NotesByOvkOnSolidityServlet; import org.tron.core.services.interfaceOnSolidity.http.TriggerConstantContractOnSolidityServlet; - @Slf4j(topic = "API") public class HttpApiOnSolidityService extends HttpService { @@ -74,6 +72,8 @@ public class HttpApiOnSolidityService extends HttpService { @Autowired private ListWitnessesOnSolidityServlet listWitnessesOnSolidityServlet; @Autowired + private GetPaginatedNowWitnessListOnSolidityServlet getPaginatedNowWitnessListOnSolidityServlet; + @Autowired private GetAssetIssueListOnSolidityServlet getAssetIssueListOnSolidityServlet; @Autowired private GetPaginatedAssetIssueListOnSolidityServlet getPaginatedAssetIssueListOnSolidityServlet; @@ -189,6 +189,8 @@ protected void addServlet(ServletContextHandler context) { context.addServlet(new ServletHolder(accountOnSolidityServlet), "/walletsolidity/getaccount"); context.addServlet(new ServletHolder(listWitnessesOnSolidityServlet), "/walletsolidity/listwitnesses"); + context.addServlet(new ServletHolder(getPaginatedNowWitnessListOnSolidityServlet), + "/walletsolidity/getpaginatednowwitnesslist"); context.addServlet(new ServletHolder(getAssetIssueListOnSolidityServlet), "/walletsolidity/getassetissuelist"); context.addServlet(new ServletHolder(getPaginatedAssetIssueListOnSolidityServlet), diff --git a/framework/src/test/java/org/tron/core/WalletTest.java b/framework/src/test/java/org/tron/core/WalletTest.java index 831490fdca1..10b0aa178f6 100644 --- a/framework/src/test/java/org/tron/core/WalletTest.java +++ b/framework/src/test/java/org/tron/core/WalletTest.java @@ -29,6 +29,8 @@ import com.google.protobuf.Any; import com.google.protobuf.ByteString; + +import java.util.ArrayList; import java.util.Arrays; import javax.annotation.Resource; import lombok.SneakyThrows; @@ -66,9 +68,12 @@ import org.tron.core.capsule.TransactionCapsule; import org.tron.core.capsule.TransactionInfoCapsule; import org.tron.core.capsule.TransactionResultCapsule; +import org.tron.core.capsule.VotesCapsule; +import org.tron.core.capsule.WitnessCapsule; import org.tron.core.config.args.Args; import org.tron.core.exception.ContractExeException; import org.tron.core.exception.ContractValidateException; +import org.tron.core.exception.MaintenanceUnavailableException; import org.tron.core.exception.NonUniqueObjectException; import org.tron.core.store.DynamicPropertiesStore; import org.tron.core.utils.ProposalUtil.ProposalType; @@ -851,6 +856,139 @@ public void testGetDelegatedResourceV2() { } } + @Test + public void testGetPaginatedNowWitnessList_Error() { + try { + // To avoid throw MaintenanceClearingException + dbManager.getChainBaseManager().getDynamicPropertiesStore().saveStateFlag(1); + wallet.getPaginatedNowWitnessList(0, 10); + Assert.fail("Should throw error when in maintenance period"); + } catch (Exception e) { + Assert.assertTrue("Should throw MaintenanceClearingException", + e instanceof MaintenanceUnavailableException); + } + dbManager.getChainBaseManager().getDynamicPropertiesStore().saveStateFlag(0); + } + + @Test + public void testGetPaginatedNowWitnessList_CornerCase() { + try { + // To avoid throw MaintenanceClearingException + dbManager.getChainBaseManager().getDynamicPropertiesStore().saveStateFlag(0); + + GrpcAPI.WitnessList witnessList = wallet.getPaginatedNowWitnessList(-100, 0); + Assert.assertTrue("Should return an empty witness list when offset is negative", + witnessList == null); + + witnessList = wallet.getPaginatedNowWitnessList(100, 0); + Assert.assertTrue("Should return an empty witness list when limit is 0", witnessList == null); + + String fakeWitnessAddressPrefix = "fake_witness"; + int fakeNumberOfWitnesses = 1000 + 10; + // Mock additional witnesses with vote counts greater than 1000 + for (int i = 0; i < fakeNumberOfWitnesses; i++) { + saveWitnessWith(fakeWitnessAddressPrefix + i, 200); + } + + witnessList = wallet.getPaginatedNowWitnessList(0, 1000000); + // Check the returned witness list should contain 1000 witnesses with descending vote count + Assert.assertTrue("Witness list should contain 1000 witnesses", + witnessList.getWitnessesCount() == 1000); + + // clean up, delete the fake witnesses + for (int i = 0; i < fakeNumberOfWitnesses; i++) { + chainBaseManager.getWitnessStore() + .delete(ByteString.copyFromUtf8(fakeWitnessAddressPrefix + i).toByteArray()); + } + } catch (MaintenanceUnavailableException e) { + Assert.fail(e.getMessage()); + } + } + + @Test + public void testGetPaginatedNowWitnessList() { + GrpcAPI.WitnessList witnessList = wallet.getWitnessList(); + logger.info(witnessList.toString()); + + // iterate through the witness list and find the existing maximum vote count + long maxVoteCount = 0L; + for (Protocol.Witness witness : witnessList.getWitnessesList()) { + if (witness.getVoteCount() > maxVoteCount) { + maxVoteCount = witness.getVoteCount(); + } + } + String fakeWitnessAddressPrefix = "fake_witness_address_for_paged_now_witness_list"; + int fakeNumberOfWitnesses = 10; + // Mock additional witnesses with vote counts greater than the maximum + for (int i = 0; i < fakeNumberOfWitnesses; i++) { + saveWitnessWith(fakeWitnessAddressPrefix + i, maxVoteCount + 1000000L); + } + + // Create a VotesCapsule to simulate the votes for the fake witnesses + VotesCapsule votesCapsule = new VotesCapsule(ByteString.copyFromUtf8(ACCOUNT_ADDRESS_ONE), + new ArrayList()); + votesCapsule.addOldVotes(ByteString.copyFromUtf8(fakeWitnessAddressPrefix + 0), 100L); + votesCapsule.addOldVotes(ByteString.copyFromUtf8(fakeWitnessAddressPrefix + 1), 50L); + votesCapsule.addNewVotes(ByteString.copyFromUtf8(fakeWitnessAddressPrefix + 2), 200L); + votesCapsule.addNewVotes(ByteString.copyFromUtf8(fakeWitnessAddressPrefix + 3), 300L); + chainBaseManager.getVotesStore().put(votesCapsule.createDbKey(), votesCapsule); + + logger.info("now request paginated witness list with 0 offset and 10 limit:"); + GrpcAPI.WitnessList witnessList2 = null; + try { + // To avoid throw MaintenanceClearingException + dbManager.getChainBaseManager().getDynamicPropertiesStore().saveStateFlag(0); + witnessList2 = wallet.getPaginatedNowWitnessList(0, 10); + } catch (MaintenanceUnavailableException e) { + Assert.fail(e.getMessage()); + } + // Check the returned witness list should contain 10 witnesses with descending vote count + Assert.assertTrue("Witness list should contain 10 witnesses", + witnessList2.getWitnessesCount() == 10); + // Check the first witness should have the maximum vote count + Assert.assertEquals("The first witness should have the maximum vote count", + fakeWitnessAddressPrefix + 3, witnessList2.getWitnesses(0).getAddress().toStringUtf8()); + Assert.assertEquals(maxVoteCount + 1000300L, witnessList2.getWitnesses(0).getVoteCount()); + // Check the second witness should have the second maximum vote count + Assert.assertEquals("The second witness", fakeWitnessAddressPrefix + 2, + witnessList2.getWitnesses(1).getAddress().toStringUtf8()); + Assert.assertEquals(maxVoteCount + 1000200L, witnessList2.getWitnesses(1).getVoteCount()); + // Check the last witness should have the least vote count + Assert.assertEquals("The tenth witness", fakeWitnessAddressPrefix + 0, + witnessList2.getWitnesses(9).getAddress().toStringUtf8()); + Assert.assertEquals(maxVoteCount + 1000000L - 100L, + witnessList2.getWitnesses(9).getVoteCount()); + + + logger.info("after paged"); + GrpcAPI.WitnessList witnessList3 = wallet.getWitnessList(); + // Check the witness list should remain unchanged after paged request + for (Protocol.Witness witness : witnessList3.getWitnessesList()) { + if (witness.getVoteCount() > maxVoteCount) { + Assert.assertTrue("Check the witness list should remain unchanged after paged request", + witness.getVoteCount() == maxVoteCount + 1000000L); + } + } + + // clean up, delete the fake witnesses + for (int i = 0; i < fakeNumberOfWitnesses; i++) { + chainBaseManager.getWitnessStore() + .delete(ByteString.copyFromUtf8(fakeWitnessAddressPrefix + i).toByteArray()); + } + chainBaseManager.getVotesStore().delete(votesCapsule.createDbKey()); + Assert.assertTrue("Clean up the mocked witness data", + wallet.getWitnessList().getWitnessesCount() == witnessList.getWitnessesCount()); + + } + + public void saveWitnessWith(String witnessAddress, long voteCount) { + WitnessCapsule witness = new WitnessCapsule( + Protocol.Witness.newBuilder() + .setAddress(ByteString.copyFromUtf8(witnessAddress)) // Convert String to ByteString + .setVoteCount(voteCount).build()); + chainBaseManager.getWitnessStore().put(witness.getAddress().toByteArray(), witness); + } + @Test public void testGetDelegatedResourceAccountIndexV2() { dbManager.getDynamicPropertiesStore().saveUnfreezeDelayDays(1L); diff --git a/framework/src/test/java/org/tron/core/db/WitnessStoreTest.java b/framework/src/test/java/org/tron/core/db/WitnessStoreTest.java index d141a5fd790..521d048f23e 100755 --- a/framework/src/test/java/org/tron/core/db/WitnessStoreTest.java +++ b/framework/src/test/java/org/tron/core/db/WitnessStoreTest.java @@ -62,12 +62,12 @@ public void testSortWitness() { List witnessAddress = allWitnesses.stream().map(WitnessCapsule::getAddress) .collect(Collectors.toList()); this.witnessStore.sortWitness(witnessAddress, false); - this.witnessStore.sortWitnesses(allWitnesses, false); + WitnessStore.sortWitnesses(allWitnesses, false); Assert.assertEquals(witnessAddress, allWitnesses.stream().map(WitnessCapsule::getAddress) .collect(Collectors.toList())); List pre = new ArrayList<>(witnessAddress); this.witnessStore.sortWitness(witnessAddress, true); - this.witnessStore.sortWitnesses(allWitnesses, true); + WitnessStore.sortWitnesses(allWitnesses, true); Assert.assertEquals(witnessAddress, allWitnesses.stream().map(WitnessCapsule::getAddress) .collect(Collectors.toList())); Assert.assertNotEquals(pre, witnessAddress); diff --git a/framework/src/test/java/org/tron/core/services/RpcApiServicesTest.java b/framework/src/test/java/org/tron/core/services/RpcApiServicesTest.java index 3af0b8fb9b2..48a25b141dc 100644 --- a/framework/src/test/java/org/tron/core/services/RpcApiServicesTest.java +++ b/framework/src/test/java/org/tron/core/services/RpcApiServicesTest.java @@ -231,6 +231,14 @@ public void testListWitnesses() { assertNotNull(blockingStubPBFT.listWitnesses(message)); } + @Test + public void testGetPaginatedNowWitnessList() { + PaginatedMessage paginatedMessage = PaginatedMessage.newBuilder() + .setOffset(0).setLimit(5).build(); + assertNotNull(blockingStubFull.getPaginatedNowWitnessList(paginatedMessage)); + assertNotNull(blockingStubSolidity.getPaginatedNowWitnessList(paginatedMessage)); + } + @Test public void testGetAssetIssueList() { EmptyMessage message = EmptyMessage.newBuilder().build(); diff --git a/framework/src/test/java/org/tron/core/services/http/HttpServletTest.java b/framework/src/test/java/org/tron/core/services/http/HttpServletTest.java index dfd4c569e3e..03cf11f39a1 100644 --- a/framework/src/test/java/org/tron/core/services/http/HttpServletTest.java +++ b/framework/src/test/java/org/tron/core/services/http/HttpServletTest.java @@ -107,6 +107,7 @@ public class HttpServletTest { private ListNodesServlet listNodesServlet; private ListProposalsServlet listProposalsServlet; private ListWitnessesServlet listWitnessesServlet; + private GetPaginatedNowWitnessListServlet getPaginatedNowWitnessListServlet; private MarketCancelOrderServlet marketCancelOrderServlet; private MarketSellAssetServlet marketSellAssetServlet; private MetricsServlet metricsServlet; @@ -244,6 +245,7 @@ public void setUp() { listNodesServlet = new ListNodesServlet(); listProposalsServlet = new ListProposalsServlet(); listWitnessesServlet = new ListWitnessesServlet(); + getPaginatedNowWitnessListServlet = new GetPaginatedNowWitnessListServlet(); marketCancelOrderServlet = new MarketCancelOrderServlet(); marketSellAssetServlet = new MarketSellAssetServlet(); metricsServlet = new MetricsServlet(); @@ -367,6 +369,7 @@ public void doGetTest() { listNodesServlet.doGet(request, response); listProposalsServlet.doGet(request, response); listWitnessesServlet.doGet(request, response); + getPaginatedNowWitnessListServlet.doGet(request, response); marketCancelOrderServlet.doGet(request, response); marketSellAssetServlet.doGet(request, response); metricsServlet.doGet(request, response); @@ -499,6 +502,7 @@ public void doPostTest() { listNodesServlet.doPost(request, response); listProposalsServlet.doPost(request, response); listWitnessesServlet.doPost(request, response); + getPaginatedNowWitnessListServlet.doPost(request, response); marketCancelOrderServlet.doPost(request, response); marketSellAssetServlet.doPost(request, response); participateAssetIssueServlet.doPost(request, response); diff --git a/protocol/src/main/protos/api/api.proto b/protocol/src/main/protos/api/api.proto index 2505fa48d6f..a67113cb606 100644 --- a/protocol/src/main/protos/api/api.proto +++ b/protocol/src/main/protos/api/api.proto @@ -499,6 +499,8 @@ service Wallet { }; }; + rpc GetPaginatedNowWitnessList (PaginatedMessage) returns (WitnessList) { + }; rpc GetDelegatedResource (DelegatedResourceMessage) returns (DelegatedResourceList) { }; @@ -808,6 +810,10 @@ service WalletSolidity { } }; }; + + rpc GetPaginatedNowWitnessList (PaginatedMessage) returns (WitnessList) { + }; + rpc GetAssetIssueList (EmptyMessage) returns (AssetIssueList) { option (google.api.http) = { post: "/walletsolidity/getassetissuelist" From 762027185318873c1e5c63d25bf01c8e3f681639 Mon Sep 17 00:00:00 2001 From: Federico2014 Date: Wed, 20 Aug 2025 15:06:55 +0800 Subject: [PATCH 33/59] feat(gradle): upgrade the maven publishing (#6367) * feat(gradle): update the publishing * feat(gradle): update the publishing * add cleanParam --- build.gradle | 8 ++++++++ .../src/main/java/org/tron/core/config/args/Args.java | 1 + jitpack.yml | 2 +- 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 8a9dd8bec75..c03308d6fca 100644 --- a/build.gradle +++ b/build.gradle @@ -118,6 +118,14 @@ subprojects { // https://docs.gradle.org/current/dsl/org.gradle.api.tasks.testing.Test.html#org.gradle.api.tasks.testing.Test:environment environment 'CI', 'true' } + + publishing { + publications { + mavenJava(MavenPublication) { + from components.java + } + } + } } task copyToParent(type: Copy) { diff --git a/framework/src/main/java/org/tron/core/config/args/Args.java b/framework/src/main/java/org/tron/core/config/args/Args.java index 92a021280c4..edd91041f62 100644 --- a/framework/src/main/java/org/tron/core/config/args/Args.java +++ b/framework/src/main/java/org/tron/core/config/args/Args.java @@ -373,6 +373,7 @@ private static Map getOptionGroup() { */ public static void setParam(final String[] args, final String confFileName) { Arch.throwUnsupportedJavaException(); + clearParam(); // reset all parameters to avoid the influence in test JCommander.newBuilder().addObject(PARAMETER).build().parse(args); if (PARAMETER.version) { printVersion(); diff --git a/jitpack.yml b/jitpack.yml index f951e400136..4065d43092c 100644 --- a/jitpack.yml +++ b/jitpack.yml @@ -1,2 +1,2 @@ install: - - ./gradlew clean -xtest -xlint -xcheck -PbinaryRelease=false install \ No newline at end of file + - ./gradlew clean -xtest -xlint -xcheck -PbinaryRelease=false publishToMavenLocal From b3ffa431a51e3d1502547976a5ce4d91350613cd Mon Sep 17 00:00:00 2001 From: halibobo1205 <82020050+halibobo1205@users.noreply.github.com> Date: Wed, 20 Aug 2025 15:08:02 +0800 Subject: [PATCH 34/59] fix(ReasonCode): change BELOW_THAN_ME from 0X24 to 0x24 (#6426) --- protocol/src/main/protos/core/Tron.proto | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocol/src/main/protos/core/Tron.proto b/protocol/src/main/protos/core/Tron.proto index 2ffefbf9f3e..f75faeeac33 100644 --- a/protocol/src/main/protos/core/Tron.proto +++ b/protocol/src/main/protos/core/Tron.proto @@ -601,7 +601,7 @@ enum ReasonCode { CONNECT_FAIL = 0x21; TOO_MANY_PEERS_WITH_SAME_IP = 0x22; LIGHT_NODE_SYNC_FAIL = 0x23; - BELOW_THAN_ME = 0X24; + BELOW_THAN_ME = 0x24; NOT_WITNESS = 0x25; NO_SUCH_MESSAGE = 0x26; UNKNOWN = 0xFF; From d6ad7384b8ef8bd30b5fcf9d7c2c5ec99b47dd6a Mon Sep 17 00:00:00 2001 From: halibobo1205 <82020050+halibobo1205@users.noreply.github.com> Date: Thu, 21 Aug 2025 14:22:40 +0800 Subject: [PATCH 35/59] refactor(db): unify database engine initialization (#6421) - Consolidate duplicate engine initialization logic from two separate classes into a single unified method - Enhance LevelDB/RocksDB compatibility detection with clearer error messages - Improve error handling for directory/file creation failures --- .../leveldb/LevelDbDataSourceImpl.java | 34 +-- .../rocksdb/RocksDbDataSourceImpl.java | 38 +-- .../tron/core/db/common/DbSourceInter.java | 42 +++ .../java/org/tron/core/config/args/Args.java | 2 +- .../common/storage/CheckOrInitEngineTest.java | 263 ++++++++++++++++++ .../leveldb/LevelDbDataSourceImplTest.java | 11 +- .../RocksDbDataSourceImplTest.java | 21 +- .../common/org/tron/common/arch/Arch.java | 7 +- .../java/common/org/tron/plugins/DbRoot.java | 24 +- .../org/tron/plugins/utils/DBUtils.java | 2 +- 10 files changed, 340 insertions(+), 104 deletions(-) create mode 100644 framework/src/test/java/org/tron/common/storage/CheckOrInitEngineTest.java rename framework/src/test/java/org/tron/common/storage/{leveldb => rocksdb}/RocksDbDataSourceImplTest.java (95%) diff --git a/chainbase/src/main/java/org/tron/common/storage/leveldb/LevelDbDataSourceImpl.java b/chainbase/src/main/java/org/tron/common/storage/leveldb/LevelDbDataSourceImpl.java index 72bdd80d64e..54cdbf96ca1 100644 --- a/chainbase/src/main/java/org/tron/common/storage/leveldb/LevelDbDataSourceImpl.java +++ b/chainbase/src/main/java/org/tron/common/storage/leveldb/LevelDbDataSourceImpl.java @@ -18,10 +18,8 @@ import static org.fusesource.leveldbjni.JniDBFactory.factory; import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Strings; import com.google.common.collect.Sets; import com.google.common.primitives.Bytes; -import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @@ -53,7 +51,6 @@ import org.tron.common.storage.WriteOptionsWrapper; import org.tron.common.storage.metric.DbStat; import org.tron.common.utils.FileUtil; -import org.tron.common.utils.PropUtil; import org.tron.common.utils.StorageUtils; import org.tron.core.db.common.DbSourceInter; import org.tron.core.db.common.iterator.StoreIterator; @@ -73,9 +70,7 @@ public class LevelDbDataSourceImpl extends DbStat implements DbSourceInter. */ + package org.tron.core.db.common; import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Strings; +import java.io.File; +import java.nio.file.Paths; import java.util.Map; import java.util.Set; +import org.tron.common.utils.FileUtil; +import org.tron.common.utils.PropUtil; import org.tron.core.db2.common.WrappedByteArray; +import org.tron.core.exception.TronError; public interface DbSourceInter extends BatchSourceInter, Iterable> { + String ENGINE_KEY = "ENGINE"; + String ENGINE_FILE = "engine.properties"; + String ROCKSDB = "ROCKSDB"; + String LEVELDB = "LEVELDB"; + String getDBName(); void setDBName(String name); @@ -53,4 +65,34 @@ public interface DbSourceInter extends BatchSourceInter, Map prefixQuery(byte[] key); + static void checkOrInitEngine(String expectedEngine, String dir, TronError.ErrCode errCode) { + String engineFile = Paths.get(dir, ENGINE_FILE).toString(); + File currentFile = new File(dir, "CURRENT"); + if (ROCKSDB.equals(expectedEngine) && currentFile.exists() + && !Paths.get(engineFile).toFile().exists()) { + // if the CURRENT file exists, but the engine.properties file does not exist, it is LevelDB + // 000003.log CURRENT LOCK MANIFEST-000002 + throw new TronError( + String.format("Cannot open %s database with %s engine.", LEVELDB, ROCKSDB), errCode); + } + if (FileUtil.createDirIfNotExists(dir)) { + if (!FileUtil.createFileIfNotExists(engineFile)) { + throw new TronError(String.format("Cannot create file: %s.", engineFile), errCode); + } + } else { + throw new TronError(String.format("Cannot create dir: %s.", dir), errCode); + } + String actualEngine = PropUtil.readProperty(engineFile, ENGINE_KEY); + // engine init + if (Strings.isNullOrEmpty(actualEngine) + && !PropUtil.writeProperty(engineFile, ENGINE_KEY, expectedEngine)) { + throw new TronError(String.format("Cannot write file: %s.", engineFile), errCode); + } + actualEngine = PropUtil.readProperty(engineFile, ENGINE_KEY); + if (!expectedEngine.equals(actualEngine)) { + throw new TronError(String.format( + "Cannot open %s database with %s engine.", + actualEngine, expectedEngine), errCode); + } + } } diff --git a/framework/src/main/java/org/tron/core/config/args/Args.java b/framework/src/main/java/org/tron/core/config/args/Args.java index edd91041f62..bbc3acfaec9 100644 --- a/framework/src/main/java/org/tron/core/config/args/Args.java +++ b/framework/src/main/java/org/tron/core/config/args/Args.java @@ -372,7 +372,7 @@ private static Map getOptionGroup() { * set parameters. */ public static void setParam(final String[] args, final String confFileName) { - Arch.throwUnsupportedJavaException(); + Arch.throwIfUnsupportedJavaVersion(); clearParam(); // reset all parameters to avoid the influence in test JCommander.newBuilder().addObject(PARAMETER).build().parse(args); if (PARAMETER.version) { diff --git a/framework/src/test/java/org/tron/common/storage/CheckOrInitEngineTest.java b/framework/src/test/java/org/tron/common/storage/CheckOrInitEngineTest.java new file mode 100644 index 00000000000..90aac10c0b6 --- /dev/null +++ b/framework/src/test/java/org/tron/common/storage/CheckOrInitEngineTest.java @@ -0,0 +1,263 @@ +package org.tron.common.storage; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mockStatic; +import static org.tron.core.db.common.DbSourceInter.ENGINE_FILE; +import static org.tron.core.db.common.DbSourceInter.ENGINE_KEY; +import static org.tron.core.db.common.DbSourceInter.LEVELDB; +import static org.tron.core.db.common.DbSourceInter.ROCKSDB; +import static org.tron.core.db.common.DbSourceInter.checkOrInitEngine; + +import com.google.common.base.Strings; +import java.io.File; +import java.io.IOException; +import java.nio.file.Paths; +import org.junit.After; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.tron.common.utils.FileUtil; +import org.tron.common.utils.PropUtil; +import org.tron.core.exception.TronError; + + +public class CheckOrInitEngineTest { + + @Rule + public TemporaryFolder temporaryFolder = new TemporaryFolder(); + + private static final String ACCOUNT = "account"; + + @After + public void clearMocks() { + Mockito.clearAllCaches(); + } + + @Test + public void testLevelDbDetectedWhenExpectingRocksDb() throws IOException { + try (MockedStatic fileUtil = mockStatic(FileUtil.class); + MockedStatic propUtil = mockStatic(PropUtil.class); + MockedStatic strings = mockStatic(Strings.class)) { + + String dir = temporaryFolder.newFolder(ACCOUNT).toString(); + File currentFile = new File(dir, "CURRENT"); + assertTrue(currentFile.createNewFile()); + TronError.ErrCode errCode = TronError.ErrCode.ROCKSDB_INIT; + TronError exception = assertThrows(TronError.class, () -> + checkOrInitEngine(ROCKSDB, dir, errCode)); + assertEquals("Cannot open LEVELDB database with ROCKSDB engine.", + exception.getMessage()); + assertEquals(errCode, exception.getErrCode()); + } + } + + @Test + public void testCannotCreateDir() { + try (MockedStatic fileUtil = mockStatic(FileUtil.class)) { + String dir = "/invalid/path/that/cannot/be/created"; + + fileUtil.when(() -> FileUtil.createDirIfNotExists(dir)).thenReturn(false); + TronError.ErrCode errCode = TronError.ErrCode.LEVELDB_INIT; + TronError exception = assertThrows(TronError.class, () -> + checkOrInitEngine(LEVELDB, dir, errCode)); + assertEquals("Cannot create dir: " + dir + ".", exception.getMessage()); + assertEquals(errCode, exception.getErrCode()); + } + } + + @Test + public void testCannotCreateEngineFile() throws IOException { + try (MockedStatic fileUtil = mockStatic(FileUtil.class)) { + String dir = temporaryFolder.newFolder().toString(); + String engineFile = Paths.get(dir, ENGINE_FILE).toString(); + fileUtil.when(() -> FileUtil.createDirIfNotExists(dir)).thenReturn(true); + fileUtil.when(() -> FileUtil.createFileIfNotExists(engineFile)).thenReturn(false); + TronError.ErrCode errCode = TronError.ErrCode.ROCKSDB_INIT; + TronError exception = assertThrows(TronError.class, () -> + checkOrInitEngine(ROCKSDB, dir, errCode)); + + assertEquals("Cannot create file: " + engineFile + ".", exception.getMessage()); + assertEquals(errCode, exception.getErrCode()); + } + } + + @Test + public void testCannotWritePropertyFile() throws IOException { + try (MockedStatic fileUtil = mockStatic(FileUtil.class); + MockedStatic propUtil = mockStatic(PropUtil.class); + MockedStatic strings = mockStatic(Strings.class)) { + + String dir = temporaryFolder.newFolder().toString(); + String engineFile = Paths.get(dir, ENGINE_FILE).toString(); + + fileUtil.when(() -> FileUtil.createDirIfNotExists(dir)).thenReturn(true); + fileUtil.when(() -> FileUtil.createFileIfNotExists(engineFile)).thenReturn(true); + + propUtil.when(() -> PropUtil.readProperty(engineFile, ENGINE_KEY)).thenReturn(null); + strings.when(() -> Strings.isNullOrEmpty(null)).thenReturn(true); + + propUtil.when(() -> PropUtil.writeProperty(engineFile, ENGINE_KEY, ROCKSDB)) + .thenReturn(false); + + TronError.ErrCode errCode = TronError.ErrCode.LEVELDB_INIT; + + TronError exception = assertThrows(TronError.class, () -> + checkOrInitEngine(ROCKSDB, dir, errCode)); + + assertEquals("Cannot write file: " + engineFile + ".", exception.getMessage()); + assertEquals(errCode, exception.getErrCode()); + } + + } + + @Test + public void testEngineMismatch() throws IOException { + try (MockedStatic fileUtil = mockStatic(FileUtil.class); + MockedStatic propUtil = mockStatic(PropUtil.class); + MockedStatic strings = mockStatic(Strings.class)) { + + String dir = temporaryFolder.newFolder(ACCOUNT).toString(); + String engineFile = Paths.get(dir, ENGINE_FILE).toString(); + + fileUtil.when(() -> FileUtil.createDirIfNotExists(dir)).thenReturn(true); + fileUtil.when(() -> FileUtil.createFileIfNotExists(engineFile)).thenReturn(true); + + propUtil.when(() -> PropUtil.readProperty(engineFile, ENGINE_KEY)).thenReturn(LEVELDB); + strings.when(() -> Strings.isNullOrEmpty(LEVELDB)).thenReturn(false); + + TronError.ErrCode errCode = TronError.ErrCode.ROCKSDB_INIT; + + TronError exception = assertThrows(TronError.class, () -> + checkOrInitEngine(ROCKSDB, dir, errCode)); + + assertEquals("Cannot open LEVELDB database with ROCKSDB engine.", + exception.getMessage()); + assertEquals(errCode, exception.getErrCode()); + } + } + + @Test + public void testSuccessfulFirstTimeInit() throws IOException { + try (MockedStatic fileUtil = mockStatic(FileUtil.class); + MockedStatic propUtil = mockStatic(PropUtil.class); + MockedStatic strings = mockStatic(Strings.class)) { + + String dir = temporaryFolder.newFolder(ACCOUNT).toString(); + String engineFile = Paths.get(dir, ENGINE_FILE).toString(); + + fileUtil.when(() -> FileUtil.createDirIfNotExists(dir)).thenReturn(true); + fileUtil.when(() -> FileUtil.createFileIfNotExists(engineFile)).thenReturn(true); + + propUtil.when(() -> PropUtil.readProperty(engineFile, ENGINE_KEY)) + .thenReturn(null) + .thenReturn(LEVELDB); + strings.when(() -> Strings.isNullOrEmpty(null)).thenReturn(true); + + propUtil.when(() -> PropUtil.writeProperty(engineFile, ENGINE_KEY, LEVELDB)) + .thenReturn(true); + + TronError.ErrCode errCode = TronError.ErrCode.LEVELDB_INIT; + checkOrInitEngine(LEVELDB, dir, errCode); + } + } + + @Test + public void testSuccessfulExistingEngine() throws IOException { + try (MockedStatic fileUtil = mockStatic(FileUtil.class); + MockedStatic propUtil = mockStatic(PropUtil.class); + MockedStatic strings = mockStatic(Strings.class)) { + + String dir = temporaryFolder.newFolder(ACCOUNT).toString(); + String engineFile = Paths.get(dir, ENGINE_FILE).toString(); + + fileUtil.when(() -> FileUtil.createDirIfNotExists(dir)).thenReturn(true); + fileUtil.when(() -> FileUtil.createFileIfNotExists(engineFile)).thenReturn(true); + propUtil.when(() -> PropUtil.readProperty(engineFile, ENGINE_KEY)).thenReturn(ROCKSDB); + strings.when(() -> Strings.isNullOrEmpty(ROCKSDB)).thenReturn(false); + + TronError.ErrCode errCode = TronError.ErrCode.ROCKSDB_INIT; + checkOrInitEngine(ROCKSDB, dir, errCode); + } + } + + @Test + /** + * 000003.log CURRENT LOCK MANIFEST-000002 + */ + public void testCurrentFileExistsWithNoEngineFile() throws IOException { + try (MockedStatic fileUtil = mockStatic(FileUtil.class); + MockedStatic propUtil = mockStatic(PropUtil.class); + MockedStatic strings = mockStatic(Strings.class)) { + + String dir = temporaryFolder.newFolder(ACCOUNT).toString(); + String engineFile = Paths.get(dir, ENGINE_FILE).toString(); + File currentFile = new File(dir, "CURRENT"); + assertTrue(currentFile.createNewFile()); + + fileUtil.when(() -> FileUtil.createDirIfNotExists(dir)).thenReturn(true); + fileUtil.when(() -> FileUtil.createFileIfNotExists(engineFile)).thenReturn(true); + propUtil.when(() -> PropUtil.readProperty(engineFile, ENGINE_KEY)).thenReturn(LEVELDB); + strings.when(() -> Strings.isNullOrEmpty(LEVELDB)).thenReturn(false); + + TronError.ErrCode errCode = TronError.ErrCode.LEVELDB_INIT; + + checkOrInitEngine(LEVELDB, dir, errCode); + } + } + + @Test + /** + * 000003.log CURRENT LOCK MANIFEST-000002 engine.properties(RocksDB) + */ + public void testCurrentFileExistsEngineFileExists() throws IOException { + try (MockedStatic fileUtil = mockStatic(FileUtil.class); + MockedStatic propUtil = mockStatic(PropUtil.class); + MockedStatic strings = mockStatic(Strings.class)) { + + String dir = temporaryFolder.newFolder(ACCOUNT).toString(); + String engineFile = Paths.get(dir, ENGINE_FILE).toString(); + + File currentFile = new File(dir, "CURRENT"); + File engineFileObj = new File(engineFile); + assertTrue(currentFile.createNewFile()); + assertTrue(engineFileObj.createNewFile()); + + + fileUtil.when(() -> FileUtil.createDirIfNotExists(dir)).thenReturn(true); + fileUtil.when(() -> FileUtil.createFileIfNotExists(engineFile)).thenReturn(true); + + propUtil.when(() -> PropUtil.readProperty(engineFile, ENGINE_KEY)).thenReturn(ROCKSDB); + strings.when(() -> Strings.isNullOrEmpty(ROCKSDB)).thenReturn(false); + + TronError.ErrCode errCode = TronError.ErrCode.ROCKSDB_INIT; + checkOrInitEngine(ROCKSDB, dir, errCode); + } + } + + @Test + public void testEmptyStringEngine() throws IOException { + try (MockedStatic fileUtil = mockStatic(FileUtil.class); + MockedStatic propUtil = mockStatic(PropUtil.class); + MockedStatic strings = mockStatic(Strings.class)) { + + String dir = temporaryFolder.newFolder("account").toString(); + String engineFile = Paths.get(dir, ENGINE_FILE).toString(); + + fileUtil.when(() -> FileUtil.createDirIfNotExists(dir)).thenReturn(true); + fileUtil.when(() -> FileUtil.createFileIfNotExists(engineFile)).thenReturn(true); + + propUtil.when(() -> PropUtil.readProperty(engineFile, ENGINE_KEY)) + .thenReturn("").thenReturn(ROCKSDB); + strings.when(() -> Strings.isNullOrEmpty("")).thenReturn(true); + + propUtil.when(() -> PropUtil.writeProperty(engineFile, ENGINE_KEY, ROCKSDB)) + .thenReturn(true); + TronError.ErrCode errCode = TronError.ErrCode.ROCKSDB_INIT; + checkOrInitEngine(ROCKSDB, dir, errCode); + } + } +} diff --git a/framework/src/test/java/org/tron/common/storage/leveldb/LevelDbDataSourceImplTest.java b/framework/src/test/java/org/tron/common/storage/leveldb/LevelDbDataSourceImplTest.java index c8b29c3020d..b5cfd15761e 100644 --- a/framework/src/test/java/org/tron/common/storage/leveldb/LevelDbDataSourceImplTest.java +++ b/framework/src/test/java/org/tron/common/storage/leveldb/LevelDbDataSourceImplTest.java @@ -421,11 +421,8 @@ public void testCheckOrInitEngine() { try { dataSource = new LevelDbDataSourceImpl(dir, "test_engine"); dataSource.initDB(); - } catch (Exception e) { - Assert.assertEquals(String.format( - "Cannot open RocksDB database '%s' with LevelDB engine." - + " Set db.engine=ROCKSDB or use LevelDB database. ", "test_engine"), - e.getMessage()); + } catch (TronError e) { + Assert.assertEquals("Cannot open ROCKSDB database with LEVELDB engine.", e.getMessage()); } } @@ -441,9 +438,7 @@ public void testLevelDbOpenRocksDb() { rocksDb.closeDB(); LevelDbDataSourceImpl levelDB = new LevelDbDataSourceImpl(StorageUtils.getOutputDirectoryByDbName(name), name); - exception.expectMessage(String.format( - "Cannot open RocksDB database '%s' with LevelDB engine." - + " Set db.engine=ROCKSDB or use LevelDB database. ", name)); + exception.expectMessage("Cannot open ROCKSDB database with LEVELDB engine."); levelDB.initDB(); } diff --git a/framework/src/test/java/org/tron/common/storage/leveldb/RocksDbDataSourceImplTest.java b/framework/src/test/java/org/tron/common/storage/rocksdb/RocksDbDataSourceImplTest.java similarity index 95% rename from framework/src/test/java/org/tron/common/storage/leveldb/RocksDbDataSourceImplTest.java rename to framework/src/test/java/org/tron/common/storage/rocksdb/RocksDbDataSourceImplTest.java index 8f42c44e3b9..3ccbb703710 100644 --- a/framework/src/test/java/org/tron/common/storage/leveldb/RocksDbDataSourceImplTest.java +++ b/framework/src/test/java/org/tron/common/storage/rocksdb/RocksDbDataSourceImplTest.java @@ -1,4 +1,4 @@ -package org.tron.common.storage.leveldb; +package org.tron.common.storage.rocksdb; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -34,7 +34,7 @@ import org.tron.common.error.TronDBException; import org.tron.common.parameter.CommonParameter; import org.tron.common.storage.WriteOptionsWrapper; -import org.tron.common.storage.rocksdb.RocksDbDataSourceImpl; +import org.tron.common.storage.leveldb.LevelDbDataSourceImpl; import org.tron.common.utils.ByteArray; import org.tron.common.utils.FileUtil; import org.tron.common.utils.PropUtil; @@ -314,11 +314,8 @@ public void testCheckOrInitEngine() { dataSource = new RocksDbDataSourceImpl(dir, "test_engine"); try { dataSource.initDB(); - } catch (Exception e) { - Assert.assertEquals(String.format( - "Cannot open LevelDB database '%s' with RocksDB engine." - + " Set db.engine=LEVELDB or use RocksDB database. ", "test_engine"), - e.getMessage()); + } catch (TronError e) { + Assert.assertEquals("Cannot open LEVELDB database with ROCKSDB engine.", e.getMessage()); } Assert.assertNull(dataSource.getDatabase()); PropUtil.writeProperty(enginePath, "ENGINE", "ROCKSDB"); @@ -448,10 +445,7 @@ public void testRocksDbOpenLevelDb() { levelDb.putData(key1, value1); levelDb.closeDB(); RocksDbDataSourceImpl rocksDb = new RocksDbDataSourceImpl(output, name); - expectedException.expectMessage( - String.format( - "Cannot open LevelDB database '%s' with RocksDB engine." - + " Set db.engine=LEVELDB or use RocksDB database. ", name)); + expectedException.expectMessage("Cannot open LEVELDB database with ROCKSDB engine."); rocksDb.initDB(); } @@ -474,10 +468,7 @@ public void testRocksDbOpenLevelDb2() { } Assert.assertFalse(engineFile.exists()); RocksDbDataSourceImpl rocksDb = new RocksDbDataSourceImpl(output, name); - expectedException.expectMessage( - String.format( - "Cannot open LevelDB database '%s' with RocksDB engine." - + " Set db.engine=LEVELDB or use RocksDB database. ", name)); + expectedException.expectMessage("Cannot open LEVELDB database with ROCKSDB engine."); rocksDb.initDB(); } diff --git a/platform/src/main/java/common/org/tron/common/arch/Arch.java b/platform/src/main/java/common/org/tron/common/arch/Arch.java index 20744d755a4..e9edf8945d4 100644 --- a/platform/src/main/java/common/org/tron/common/arch/Arch.java +++ b/platform/src/main/java/common/org/tron/common/arch/Arch.java @@ -69,7 +69,7 @@ public static boolean isJava8() { return javaSpecificationVersion().equals("1.8"); } - public static void throwUnsupportedJavaException() { + public static void throwIfUnsupportedJavaVersion() { if (isX86() && !isJava8()) { logger.info(withAll()); throw new UnsupportedOperationException(String.format( @@ -78,9 +78,10 @@ public static void throwUnsupportedJavaException() { } } - public static void throwUnsupportedArm64Exception() { + public static void throwIfUnsupportedArm64Exception(String message) { if (isArm64()) { - throw new UnsupportedOperationException("unsupported on " + getOsArch() + " architecture"); + throw new UnsupportedOperationException( + message + ": unsupported on " + getOsArch() + " architecture"); } } } diff --git a/plugins/src/main/java/common/org/tron/plugins/DbRoot.java b/plugins/src/main/java/common/org/tron/plugins/DbRoot.java index 7c33219e180..45854bbebdc 100644 --- a/plugins/src/main/java/common/org/tron/plugins/DbRoot.java +++ b/plugins/src/main/java/common/org/tron/plugins/DbRoot.java @@ -67,16 +67,24 @@ public Integer call() throws Exception { .errorText("Specify at least one exit database: --db dbName.")); return 404; } - List task = ProgressBar.wrap(dbs.stream(), "root task").parallel() - .map(this::calcMerkleRoot).collect(Collectors.toList()); - task.forEach(this::printInfo); - int code = (int) task.stream().filter(r -> r.code == 1).count(); - if (code > 0) { + try { + List task = ProgressBar.wrap(dbs.stream(), "root task").parallel() + .map(this::calcMerkleRoot).collect(Collectors.toList()); + task.forEach(this::printInfo); + int code = (int) task.stream().filter(r -> r.code == 1).count(); + if (code > 0) { + spec.commandLine().getErr().println(spec.commandLine().getColorScheme() + .errorText("There are some errors, please check toolkit.log for detail.")); + } + spec.commandLine().getOut().println("root task done."); + return code; + } catch (Exception e) { + logger.error("{}", e); spec.commandLine().getErr().println(spec.commandLine().getColorScheme() - .errorText("There are some errors, please check toolkit.log for detail.")); + .errorText(e.getMessage())); + spec.commandLine().usage(System.out); + return 1; } - spec.commandLine().getOut().println("root task done."); - return code; } private Ret calcMerkleRoot(String name) { diff --git a/plugins/src/main/java/common/org/tron/plugins/utils/DBUtils.java b/plugins/src/main/java/common/org/tron/plugins/utils/DBUtils.java index 507a8ad798e..e003b098a43 100644 --- a/plugins/src/main/java/common/org/tron/plugins/utils/DBUtils.java +++ b/plugins/src/main/java/common/org/tron/plugins/utils/DBUtils.java @@ -66,7 +66,7 @@ static Operator valueOf(byte b) { public static final String ROCKSDB = "ROCKSDB"; public static DB newLevelDb(Path db) throws IOException { - Arch.throwUnsupportedArm64Exception(); + Arch.throwIfUnsupportedArm64Exception(LEVELDB); File file = db.toFile(); org.iq80.leveldb.Options dbOptions = newDefaultLevelDbOptions(); if (MARKET_PAIR_PRICE_TO_ORDER.equalsIgnoreCase(file.getName())) { From 9e456c3b7f6dcc5348576cf23dd51517e6d13c5c Mon Sep 17 00:00:00 2001 From: Federico2014 Date: Thu, 21 Aug 2025 15:08:24 +0800 Subject: [PATCH 36/59] fix(test): fix the ZksnarkInitTest (#6427) * fix the ZksnarkInitTest * restore the original value --- .../java/org/tron/core/exception/TronErrorTest.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/framework/src/test/java/org/tron/core/exception/TronErrorTest.java b/framework/src/test/java/org/tron/core/exception/TronErrorTest.java index 98d5596e251..6976e73b013 100644 --- a/framework/src/test/java/org/tron/core/exception/TronErrorTest.java +++ b/framework/src/test/java/org/tron/core/exception/TronErrorTest.java @@ -9,10 +9,12 @@ import com.typesafe.config.ConfigFactory; import com.typesafe.config.ConfigObject; import java.io.IOException; +import java.lang.reflect.Field; import java.nio.file.Path; import java.util.Collections; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; import org.junit.After; import org.junit.Assert; import org.junit.Rule; @@ -57,7 +59,13 @@ public void testTronError() { } @Test - public void ZksnarkInitTest() { + public void ZksnarkInitTest() throws IllegalAccessException, NoSuchFieldException { + Field field = ZksnarkInitService.class.getDeclaredField("initialized"); + field.setAccessible(true); + AtomicBoolean atomicBoolean = (AtomicBoolean) field.get(null); + boolean originalValue = atomicBoolean.get(); + atomicBoolean.set(false); + try (MockedStatic mock = mockStatic(JLibrustzcash.class)) { mock.when(() -> JLibrustzcash.librustzcashInitZksnarkParams(any())) .thenAnswer(invocation -> { @@ -66,6 +74,8 @@ public void ZksnarkInitTest() { TronError thrown = assertThrows(TronError.class, ZksnarkInitService::librustzcashInitZksnarkParams); assertEquals(TronError.ErrCode.ZCASH_INIT, thrown.getErrCode()); + } finally { + atomicBoolean.set(originalValue); } } From af0e59f1ca8efb50544b0193b865be7e8f1ff501 Mon Sep 17 00:00:00 2001 From: raymond <109072038+raymondliu0711@users.noreply.github.com> Date: Thu, 21 Aug 2025 18:11:04 +0800 Subject: [PATCH 37/59] feat(tvm): implement TIP-6780 (#6383) * feat(tvm): tip-6780: selfdestruct in same transaction * test(tvm): add test for tip-6780 * test(tvm): fix import style * test(tvm): fix suicide energy cost test and suicideAction test. fix checkstyle warning --- .../org/tron/core/utils/ProposalUtil.java | 18 ++- .../java/org/tron/core/vm/EnergyCost.java | 9 ++ .../org/tron/core/vm/OperationActions.java | 15 +++ .../org/tron/core/vm/OperationRegistry.java | 11 ++ .../org/tron/core/vm/config/ConfigLoader.java | 1 + .../tron/core/vm/program/ContractState.java | 10 ++ .../org/tron/core/vm/program/Program.java | 108 ++++++++++++++++++ .../tron/core/vm/repository/Repository.java | 4 + .../core/vm/repository/RepositoryImpl.java | 33 ++++++ .../core/store/DynamicPropertiesStore.java | 19 +++ .../common/parameter/CommonParameter.java | 4 + .../src/main/java/org/tron/core/Constant.java | 5 + .../org/tron/core/vm/config/VMConfig.java | 10 ++ .../src/main/java/org/tron/core/Wallet.java | 5 + .../java/org/tron/core/config/args/Args.java | 5 + .../tron/core/consensus/ProposalService.java | 4 + .../java/org/tron/common/ParameterTest.java | 2 + .../common/runtime/vm/OperationsTest.java | 91 +++++++++++++++ .../core/actuator/utils/ProposalUtilTest.java | 54 +++++++++ 19 files changed, 407 insertions(+), 1 deletion(-) diff --git a/actuator/src/main/java/org/tron/core/utils/ProposalUtil.java b/actuator/src/main/java/org/tron/core/utils/ProposalUtil.java index 1df35154d7d..6df6ba80a80 100644 --- a/actuator/src/main/java/org/tron/core/utils/ProposalUtil.java +++ b/actuator/src/main/java/org/tron/core/utils/ProposalUtil.java @@ -841,6 +841,21 @@ public static void validator(DynamicPropertiesStore dynamicPropertiesStore, } break; } + case ALLOW_TVM_SELFDESTRUCT_RESTRICTION: { + if (!forkController.pass(ForkBlockVersionEnum.VERSION_4_8_1)) { + throw new ContractValidateException( + "Bad chain parameter id [ALLOW_TVM_SELFDESTRUCT_RESTRICTION]"); + } + if (dynamicPropertiesStore.allowTvmSelfdestructRestriction()) { + throw new ContractValidateException( + "[ALLOW_TVM_SELFDESTRUCT_RESTRICTION] has been valid, no need to propose again"); + } + if (value != 1) { + throw new ContractValidateException( + "This value[ALLOW_TVM_SELFDESTRUCT_RESTRICTION] is only allowed to be 1"); + } + break; + } case PROPOSAL_EXPIRE_TIME: { if (!forkController.pass(ForkBlockVersionEnum.VERSION_4_8_1)) { throw new ContractValidateException( @@ -938,7 +953,8 @@ public enum ProposalType { // current value, value range ALLOW_STRICT_MATH(87), // 0, 1 CONSENSUS_LOGIC_OPTIMIZATION(88), // 0, 1 ALLOW_TVM_BLOB(89), // 0, 1 - PROPOSAL_EXPIRE_TIME(92); // (0, 31536003000) + PROPOSAL_EXPIRE_TIME(92), // (0, 31536003000) + ALLOW_TVM_SELFDESTRUCT_RESTRICTION(94); // 0, 1 private long code; diff --git a/actuator/src/main/java/org/tron/core/vm/EnergyCost.java b/actuator/src/main/java/org/tron/core/vm/EnergyCost.java index b758438c940..d47f716943f 100644 --- a/actuator/src/main/java/org/tron/core/vm/EnergyCost.java +++ b/actuator/src/main/java/org/tron/core/vm/EnergyCost.java @@ -55,6 +55,7 @@ public class EnergyCost { private static final long EXT_CODE_SIZE = 20; private static final long EXT_CODE_HASH = 400; private static final long SUICIDE = 0; + private static final long SUICIDE_V2 = 5000; private static final long STOP = 0; private static final long CREATE_DATA = 200; private static final long TLOAD = 100; @@ -289,6 +290,14 @@ public static long getSuicideCost2(Program program) { return getSuicideCost(program); } + public static long getSuicideCost3(Program program) { + DataWord inheritorAddress = program.getStack().peek(); + if (isDeadAccount(program, inheritorAddress)) { + return SUICIDE_V2 + NEW_ACCT_CALL; + } + return SUICIDE_V2; + } + public static long getBalanceCost(Program ignored) { return BALANCE; } diff --git a/actuator/src/main/java/org/tron/core/vm/OperationActions.java b/actuator/src/main/java/org/tron/core/vm/OperationActions.java index f10fb37dd7e..0d978743a5e 100644 --- a/actuator/src/main/java/org/tron/core/vm/OperationActions.java +++ b/actuator/src/main/java/org/tron/core/vm/OperationActions.java @@ -1072,4 +1072,19 @@ public static void suicideAction(Program program) { program.stop(); } + public static void suicideAction2(Program program) { + if (program.isStaticCall()) { + throw new Program.StaticCallModificationException(); + } + + if (!program.canSuicide2()) { + program.getResult().setRevert(); + } else { + DataWord address = program.stackPop(); + program.suicide2(address); + } + + program.stop(); + } + } diff --git a/actuator/src/main/java/org/tron/core/vm/OperationRegistry.java b/actuator/src/main/java/org/tron/core/vm/OperationRegistry.java index be29238a775..f6140107efb 100644 --- a/actuator/src/main/java/org/tron/core/vm/OperationRegistry.java +++ b/actuator/src/main/java/org/tron/core/vm/OperationRegistry.java @@ -79,6 +79,10 @@ public static JumpTable getTable() { adjustForFairEnergy(table); } + if (VMConfig.allowTvmSelfdestructRestriction()) { + adjustSelfdestruct(table); + } + return table; } @@ -695,4 +699,11 @@ public static void appendCancunOperations(JumpTable table) { OperationActions::blobBaseFeeAction, tvmBlobProposal)); } + + public static void adjustSelfdestruct(JumpTable table) { + table.set(new Operation( + Op.SUICIDE, 1, 0, + EnergyCost::getSuicideCost3, + OperationActions::suicideAction2)); + } } diff --git a/actuator/src/main/java/org/tron/core/vm/config/ConfigLoader.java b/actuator/src/main/java/org/tron/core/vm/config/ConfigLoader.java index 1ec8a75d669..e0ebca329cd 100644 --- a/actuator/src/main/java/org/tron/core/vm/config/ConfigLoader.java +++ b/actuator/src/main/java/org/tron/core/vm/config/ConfigLoader.java @@ -44,6 +44,7 @@ public static void load(StoreFactory storeFactory) { VMConfig.initAllowTvmCancun(ds.getAllowTvmCancun()); VMConfig.initDisableJavaLangMath(ds.getConsensusLogicOptimization()); VMConfig.initAllowTvmBlob(ds.getAllowTvmBlob()); + VMConfig.initAllowTvmSelfdestructRestriction(ds.getAllowTvmSelfdestructRestriction()); } } } diff --git a/actuator/src/main/java/org/tron/core/vm/program/ContractState.java b/actuator/src/main/java/org/tron/core/vm/program/ContractState.java index e095201e393..c6347b9a072 100644 --- a/actuator/src/main/java/org/tron/core/vm/program/ContractState.java +++ b/actuator/src/main/java/org/tron/core/vm/program/ContractState.java @@ -121,6 +121,16 @@ public void updateContractState(byte[] address, ContractStateCapsule contractSta repository.updateContractState(address, contractStateCapsule); } + @Override + public void putNewContract(byte[] address) { + repository.putNewContract(address); + } + + @Override + public boolean isNewContract(byte[] address) { + return repository.isNewContract(address); + } + @Override public void updateAccount(byte[] address, AccountCapsule accountCapsule) { repository.updateAccount(address, accountCapsule); diff --git a/actuator/src/main/java/org/tron/core/vm/program/Program.java b/actuator/src/main/java/org/tron/core/vm/program/Program.java index 5da0b02ecb7..59ac8314683 100644 --- a/actuator/src/main/java/org/tron/core/vm/program/Program.java +++ b/actuator/src/main/java/org/tron/core/vm/program/Program.java @@ -515,6 +515,72 @@ public void suicide(DataWord obtainerAddress) { getResult().addDeleteAccount(this.getContractAddress()); } + public void suicide2(DataWord obtainerAddress) { + + byte[] owner = getContextAddress(); + boolean isNewContract = getContractState().isNewContract(owner); + if (isNewContract) { + suicide(obtainerAddress); + return; + } + + byte[] obtainer = obtainerAddress.toTronAddress(); + + long balance = getContractState().getBalance(owner); + + if (logger.isDebugEnabled()) { + logger.debug("Transfer to: [{}] heritage: [{}]", + Hex.toHexString(obtainer), + balance); + } + + increaseNonce(); + + InternalTransaction internalTx = addInternalTx(null, owner, obtainer, balance, null, + "suicide", nonce, getContractState().getAccount(owner).getAssetMapV2()); + + if (FastByteComparisons.isEqual(owner, obtainer)) { + return; + } + + if (VMConfig.allowTvmVote()) { + withdrawRewardAndCancelVote(owner, getContractState()); + balance = getContractState().getBalance(owner); + if (internalTx != null && balance != internalTx.getValue()) { + internalTx.setValue(balance); + } + } + + // transfer balance and trc10 + createAccountIfNotExist(getContractState(), obtainer); + try { + MUtil.transfer(getContractState(), owner, obtainer, balance); + if (VMConfig.allowTvmTransferTrc10()) { + MUtil.transferAllToken(getContractState(), owner, obtainer); + } + } catch (ContractValidateException e) { + if (VMConfig.allowTvmConstantinople()) { + throw new TransferException( + "transfer all token or transfer all trx failed in suicide: %s", e.getMessage()); + } + throw new BytecodeExecutionException("transfer failure"); + } + + // transfer freeze + if (VMConfig.allowTvmFreeze()) { + transferDelegatedResourceToInheritor(owner, obtainer, getContractState()); + } + + // transfer freezeV2 + if (VMConfig.allowTvmFreezeV2()) { + long expireUnfrozenBalance = + transferFrozenV2BalanceToInheritor(owner, obtainer, getContractState()); + if (expireUnfrozenBalance > 0 && internalTx != null) { + internalTx.setValue(internalTx.getValue() + expireUnfrozenBalance); + } + } + } + public Repository getContractState() { return this.contractState; } @@ -544,6 +610,11 @@ private void transferDelegatedResourceToInheritor(byte[] ownerAddr, byte[] inher // transfer all kinds of frozen balance to BlackHole repo.addBalance(inheritorAddr, frozenBalanceForBandwidthOfOwner + frozenBalanceForEnergyOfOwner); + + if (VMConfig.allowTvmSelfdestructRestriction()) { + clearOwnerFreeze(ownerCapsule); + repo.updateAccount(ownerAddr, ownerCapsule); + } } private long transferFrozenV2BalanceToInheritor(byte[] ownerAddr, byte[] inheritorAddr, Repository repo) { @@ -609,6 +680,11 @@ private long transferFrozenV2BalanceToInheritor(byte[] ownerAddr, byte[] inherit return expireUnfrozenBalance; } + private void clearOwnerFreeze(AccountCapsule ownerCapsule) { + ownerCapsule.setFrozenForBandwidth(0, 0); + ownerCapsule.setFrozenForEnergy(0, 0); + } + private void clearOwnerFreezeV2(AccountCapsule ownerCapsule) { ownerCapsule.clearFrozenV2(); ownerCapsule.setNetUsage(0); @@ -666,6 +742,38 @@ public boolean canSuicide() { // return freezeCheck && voteCheck; } + public boolean canSuicide2() { + byte[] owner = getContextAddress(); + AccountCapsule accountCapsule = getContractState().getAccount(owner); + + return freezeV1Check(accountCapsule) && freezeV2Check(accountCapsule); + } + + private boolean freezeV1Check(AccountCapsule accountCapsule) { + if (!VMConfig.allowTvmFreeze()) { + return true; + } + + // check freeze + long now = getContractState().getDynamicPropertiesStore().getLatestBlockHeaderTimestamp(); + // bandwidth + if (accountCapsule.getFrozenCount() > 0 + && accountCapsule.getFrozenList().stream() + .anyMatch(frozen -> frozen.getExpireTime() > now)) { + return false; + } + // energy + Protocol.Account.Frozen frozenEnergy = + accountCapsule.getAccountResource().getFrozenBalanceForEnergy(); + if (frozenEnergy.getFrozenBalance() > 0 && frozenEnergy.getExpireTime() > now) { + return false; + } + + // check delegate + return accountCapsule.getDelegatedFrozenBalanceForBandwidth() == 0 + && accountCapsule.getDelegatedFrozenBalanceForEnergy() == 0; + } + private boolean freezeV2Check(AccountCapsule accountCapsule) { if (!VMConfig.allowTvmFreezeV2()) { return true; diff --git a/actuator/src/main/java/org/tron/core/vm/repository/Repository.java b/actuator/src/main/java/org/tron/core/vm/repository/Repository.java index 664ee26ee92..8f91d59d0b8 100644 --- a/actuator/src/main/java/org/tron/core/vm/repository/Repository.java +++ b/actuator/src/main/java/org/tron/core/vm/repository/Repository.java @@ -55,6 +55,10 @@ public interface Repository { void updateContractState(byte[] address, ContractStateCapsule contractStateCapsule); + void putNewContract(byte[] address); + + boolean isNewContract(byte[] address); + void updateAccount(byte[] address, AccountCapsule accountCapsule); void updateDynamicProperty(byte[] word, BytesCapsule bytesCapsule); diff --git a/actuator/src/main/java/org/tron/core/vm/repository/RepositoryImpl.java b/actuator/src/main/java/org/tron/core/vm/repository/RepositoryImpl.java index a064cbf6c8a..9de7c0691ba 100644 --- a/actuator/src/main/java/org/tron/core/vm/repository/RepositoryImpl.java +++ b/actuator/src/main/java/org/tron/core/vm/repository/RepositoryImpl.java @@ -9,6 +9,7 @@ import com.google.common.collect.HashBasedTable; import com.google.protobuf.ByteString; import java.util.HashMap; +import java.util.HashSet; import java.util.Optional; import lombok.Getter; import lombok.extern.slf4j.Slf4j; @@ -135,6 +136,7 @@ public class RepositoryImpl implements Repository { private final HashMap> delegationCache = new HashMap<>(); private final HashMap> delegatedResourceAccountIndexCache = new HashMap<>(); private final HashBasedTable> transientStorage = HashBasedTable.create(); + private final HashSet newContractCache = new HashSet<>(); public static void removeLruCache(byte[] address) { } @@ -479,6 +481,7 @@ public void deleteContract(byte[] address) { public void createContract(byte[] address, ContractCapsule contractCapsule) { contractCache.put(Key.create(address), Value.create(contractCapsule, Type.CREATE)); + putNewContract(address); } @Override @@ -533,6 +536,29 @@ public void updateContractState(byte[] address, ContractStateCapsule contractSta Value.create(contractStateCapsule, Type.DIRTY)); } + @Override + public void putNewContract(byte[] address) { + newContractCache.add(Key.create(address)); + } + + @Override + public boolean isNewContract(byte[] address) { + Key key = Key.create(address); + if (newContractCache.contains(key)) { + return true; + } + + if (parent != null) { + boolean isNew = parent.isNewContract(address); + if (isNew) { + newContractCache.add(key); + } + return isNew; + } else { + return false; + } + } + @Override public void updateAccount(byte[] address, AccountCapsule accountCapsule) { accountCache.put(Key.create(address), @@ -740,6 +766,7 @@ public void commit() { commitDelegationCache(repository); commitDelegatedResourceAccountIndexCache(repository); commitTransientStorage(repository); + commitNewContractCache(repository); } @Override @@ -1060,6 +1087,12 @@ public void commitTransientStorage(Repository deposit) { } } + public void commitNewContractCache(Repository deposit) { + if (deposit != null) { + newContractCache.forEach(key -> deposit.putNewContract(key.getData())); + } + } + /** * Get the block id from the number. */ diff --git a/chainbase/src/main/java/org/tron/core/store/DynamicPropertiesStore.java b/chainbase/src/main/java/org/tron/core/store/DynamicPropertiesStore.java index bee13782e53..91e8723bd28 100644 --- a/chainbase/src/main/java/org/tron/core/store/DynamicPropertiesStore.java +++ b/chainbase/src/main/java/org/tron/core/store/DynamicPropertiesStore.java @@ -235,6 +235,9 @@ public class DynamicPropertiesStore extends TronStoreWithRevoking private static final byte[] ALLOW_TVM_BLOB = "ALLOW_TVM_BLOB".getBytes(); private static final byte[] PROPOSAL_EXPIRE_TIME = "PROPOSAL_EXPIRE_TIME".getBytes(); + private static final byte[] ALLOW_TVM_SELFDESTRUCT_RESTRICTION = + "ALLOW_TVM_SELFDESTRUCT_RESTRICTION".getBytes(); + @Autowired private DynamicPropertiesStore(@Value("properties") String dbName) { super(dbName); @@ -2949,6 +2952,22 @@ public long getAllowTvmBlob() { .orElse(CommonParameter.getInstance().getAllowTvmBlob()); } + + public long getAllowTvmSelfdestructRestriction() { + return Optional.ofNullable(getUnchecked(ALLOW_TVM_SELFDESTRUCT_RESTRICTION)) + .map(BytesCapsule::getData) + .map(ByteArray::toLong) + .orElse(CommonParameter.getInstance().getAllowTvmSelfdestructRestriction()); + } + + public void saveAllowTvmSelfdestructRestriction(long value) { + this.put(ALLOW_TVM_SELFDESTRUCT_RESTRICTION, new BytesCapsule(ByteArray.fromLong(value))); + } + + public boolean allowTvmSelfdestructRestriction() { + return getAllowTvmSelfdestructRestriction() == 1L; + } + public void saveProposalExpireTime(long proposalExpireTime) { this.put(PROPOSAL_EXPIRE_TIME, new BytesCapsule(ByteArray.fromLong(proposalExpireTime))); } diff --git a/common/src/main/java/org/tron/common/parameter/CommonParameter.java b/common/src/main/java/org/tron/common/parameter/CommonParameter.java index e32708a77e6..9e8c3682e66 100644 --- a/common/src/main/java/org/tron/common/parameter/CommonParameter.java +++ b/common/src/main/java/org/tron/common/parameter/CommonParameter.java @@ -741,6 +741,10 @@ public class CommonParameter { @Setter public long allowTvmBlob; + @Getter + @Setter + public long allowTvmSelfdestructRestriction; + private static double calcMaxTimeRatio() { //return max(2.0, min(5.0, 5 * 4.0 / max(Runtime.getRuntime().availableProcessors(), 1))); return 5.0; diff --git a/common/src/main/java/org/tron/core/Constant.java b/common/src/main/java/org/tron/core/Constant.java index 824373b1328..3d5c56dbbf0 100644 --- a/common/src/main/java/org/tron/core/Constant.java +++ b/common/src/main/java/org/tron/core/Constant.java @@ -418,5 +418,10 @@ public class Constant { public static final String COMMITTEE_ALLOW_TVM_CANCUN = "committee.allowTvmCancun"; public static final String COMMITTEE_ALLOW_TVM_BLOB = "committee.allowTvmBlob"; + + public static final String COMMITTEE_ALLOW_TVM_SELFDESTRUCT_RESTRICTION = + "committee.allowTvmSelfdestructRestriction"; + public static final String COMMITTEE_PROPOSAL_EXPIRE_TIME = "committee.proposalExpireTime"; + } diff --git a/common/src/main/java/org/tron/core/vm/config/VMConfig.java b/common/src/main/java/org/tron/core/vm/config/VMConfig.java index 2bdbeb78b92..578827b2f8c 100644 --- a/common/src/main/java/org/tron/core/vm/config/VMConfig.java +++ b/common/src/main/java/org/tron/core/vm/config/VMConfig.java @@ -59,6 +59,8 @@ public class VMConfig { private static boolean ALLOW_TVM_BLOB = false; + private static boolean ALLOW_TVM_SELFDESTRUCT_RESTRICTION = false; + private VMConfig() { } @@ -166,6 +168,10 @@ public static void initAllowTvmBlob(long allow) { ALLOW_TVM_BLOB = allow == 1; } + public static void initAllowTvmSelfdestructRestriction(long allow) { + ALLOW_TVM_SELFDESTRUCT_RESTRICTION = allow == 1; + } + public static boolean getEnergyLimitHardFork() { return CommonParameter.ENERGY_LIMIT_HARD_FORK; } @@ -261,4 +267,8 @@ public static boolean disableJavaLangMath() { public static boolean allowTvmBlob() { return ALLOW_TVM_BLOB; } + + public static boolean allowTvmSelfdestructRestriction() { + return ALLOW_TVM_SELFDESTRUCT_RESTRICTION; + } } diff --git a/framework/src/main/java/org/tron/core/Wallet.java b/framework/src/main/java/org/tron/core/Wallet.java index b433352b471..5397648d106 100755 --- a/framework/src/main/java/org/tron/core/Wallet.java +++ b/framework/src/main/java/org/tron/core/Wallet.java @@ -1495,6 +1495,11 @@ public Protocol.ChainParameters getChainParameters() { .setValue(dbManager.getDynamicPropertiesStore().getAllowTvmBlob()) .build()); + builder.addChainParameter(Protocol.ChainParameters.ChainParameter.newBuilder() + .setKey("getAllowTvmSelfdestructRestriction") + .setValue(dbManager.getDynamicPropertiesStore().getAllowTvmSelfdestructRestriction()) + .build()); + builder.addChainParameter(Protocol.ChainParameters.ChainParameter.newBuilder() .setKey("getProposalExpireTime") .setValue(dbManager.getDynamicPropertiesStore().getProposalExpireTime()) diff --git a/framework/src/main/java/org/tron/core/config/args/Args.java b/framework/src/main/java/org/tron/core/config/args/Args.java index bbc3acfaec9..d2b8b00926a 100644 --- a/framework/src/main/java/org/tron/core/config/args/Args.java +++ b/framework/src/main/java/org/tron/core/config/args/Args.java @@ -250,6 +250,7 @@ public static void clearParam() { PARAMETER.consensusLogicOptimization = 0; PARAMETER.allowTvmCancun = 0; PARAMETER.allowTvmBlob = 0; + PARAMETER.allowTvmSelfdestructRestriction = 0; } /** @@ -1263,6 +1264,10 @@ public static void setParam(final Config config) { config.hasPath(Constant.COMMITTEE_ALLOW_TVM_BLOB) ? config .getInt(Constant.COMMITTEE_ALLOW_TVM_BLOB) : 0; + PARAMETER.allowTvmSelfdestructRestriction = + config.hasPath(Constant.COMMITTEE_ALLOW_TVM_SELFDESTRUCT_RESTRICTION) ? config + .getInt(Constant.COMMITTEE_ALLOW_TVM_SELFDESTRUCT_RESTRICTION) : 0; + logConfig(); } diff --git a/framework/src/main/java/org/tron/core/consensus/ProposalService.java b/framework/src/main/java/org/tron/core/consensus/ProposalService.java index d6cf0e5fe67..51d53f6a59e 100644 --- a/framework/src/main/java/org/tron/core/consensus/ProposalService.java +++ b/framework/src/main/java/org/tron/core/consensus/ProposalService.java @@ -384,6 +384,10 @@ public static boolean process(Manager manager, ProposalCapsule proposalCapsule) manager.getDynamicPropertiesStore().saveAllowTvmBlob(entry.getValue()); break; } + case ALLOW_TVM_SELFDESTRUCT_RESTRICTION: { + manager.getDynamicPropertiesStore().saveAllowTvmSelfdestructRestriction(entry.getValue()); + break; + } case PROPOSAL_EXPIRE_TIME: { manager.getDynamicPropertiesStore().saveProposalExpireTime(entry.getValue()); break; diff --git a/framework/src/test/java/org/tron/common/ParameterTest.java b/framework/src/test/java/org/tron/common/ParameterTest.java index b16be405f61..03fdf462b26 100644 --- a/framework/src/test/java/org/tron/common/ParameterTest.java +++ b/framework/src/test/java/org/tron/common/ParameterTest.java @@ -316,5 +316,7 @@ public void testCommonParameter() { assertEquals(1, parameter.getAllowEnergyAdjustment()); parameter.setMaxCreateAccountTxSize(1000); assertEquals(1000, parameter.getMaxCreateAccountTxSize()); + parameter.setAllowTvmSelfdestructRestriction(1); + assertEquals(1, parameter.getAllowTvmSelfdestructRestriction()); } } diff --git a/framework/src/test/java/org/tron/common/runtime/vm/OperationsTest.java b/framework/src/test/java/org/tron/common/runtime/vm/OperationsTest.java index 3315005b7d2..b447af87bc2 100644 --- a/framework/src/test/java/org/tron/common/runtime/vm/OperationsTest.java +++ b/framework/src/test/java/org/tron/common/runtime/vm/OperationsTest.java @@ -1,6 +1,8 @@ package org.tron.common.runtime.vm; import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.tron.core.config.Parameter.ChainConstant.FROZEN_PERIOD; import java.util.List; import java.util.Random; @@ -13,12 +15,14 @@ import org.junit.BeforeClass; import org.junit.Ignore; import org.junit.Test; +import org.mockito.Mockito; import org.springframework.util.StringUtils; import org.tron.common.BaseTest; import org.tron.common.parameter.CommonParameter; import org.tron.common.runtime.InternalTransaction; import org.tron.common.utils.DecodeUtil; import org.tron.core.Constant; +import org.tron.core.capsule.AccountCapsule; import org.tron.core.config.args.Args; import org.tron.core.exception.ContractValidateException; import org.tron.core.store.StoreFactory; @@ -26,12 +30,14 @@ import org.tron.core.vm.JumpTable; import org.tron.core.vm.Op; import org.tron.core.vm.Operation; +import org.tron.core.vm.OperationActions; import org.tron.core.vm.OperationRegistry; import org.tron.core.vm.VM; import org.tron.core.vm.config.ConfigLoader; import org.tron.core.vm.config.VMConfig; import org.tron.core.vm.program.Program; import org.tron.core.vm.program.invoke.ProgramInvokeMockImpl; +import org.tron.core.vm.repository.Repository; import org.tron.protos.Protocol; @Slf4j @@ -886,6 +892,12 @@ public void testSuicideCost() throws ContractValidateException { Assert.assertEquals(25000, EnergyCost.getSuicideCost2(program)); invoke.getDeposit().createAccount(receiver2, Protocol.AccountType.Normal); Assert.assertEquals(0, EnergyCost.getSuicideCost2(program)); + + byte[] receiver3 = generateRandomAddress(); + program.stackPush(new DataWord(receiver3)); + Assert.assertEquals(30000, EnergyCost.getSuicideCost3(program)); + invoke.getDeposit().createAccount(receiver3, Protocol.AccountType.Normal); + Assert.assertEquals(5000, EnergyCost.getSuicideCost3(program)); } @Test @@ -911,6 +923,85 @@ public void testSuicideAction() throws ContractValidateException { VMConfig.initAllowEnergyAdjustment(0); } + @Test + public void testCanSuicide2() throws ContractValidateException { + VMConfig.initAllowTvmFreeze(1); + VMConfig.initAllowTvmFreezeV2(1); + + byte[] contractAddr = Hex.decode("41471fd3ad3e9eeadeec4608b92d16ce6b500704cc"); + invoke = new ProgramInvokeMockImpl(StoreFactory.getInstance(), new byte[0], contractAddr); + + program = new Program(null, null, invoke, + new InternalTransaction( + Protocol.Transaction.getDefaultInstance(), + InternalTransaction.TrxType.TRX_UNKNOWN_TYPE)); + program.getContractState().createAccount( + program.getContextAddress(), Protocol.AccountType.Contract); + Assert.assertTrue(program.canSuicide2()); + + long nowInMs = + program.getContractState().getDynamicPropertiesStore().getLatestBlockHeaderTimestamp(); + long expireTime = nowInMs + FROZEN_PERIOD; + AccountCapsule owner = program.getContractState().getAccount(program.getContextAddress()); + owner.setFrozenForEnergy(1000000, expireTime); + program.getContractState().updateAccount(program.getContextAddress(), owner); + Assert.assertFalse(program.canSuicide2()); + + VMConfig.initAllowTvmFreeze(0); + VMConfig.initAllowTvmFreezeV2(0); + } + + @Test + public void testSuicideAction2() throws ContractValidateException { + byte[] contractAddr = Hex.decode("41471fd3ad3e9eeadeec4608b92d16ce6b500704cc"); + invoke = new ProgramInvokeMockImpl(StoreFactory.getInstance(), new byte[0], contractAddr); + Assert.assertTrue(invoke.getDeposit().isNewContract(contractAddr)); + + program = new Program(null, null, invoke, + new InternalTransaction( + Protocol.Transaction.getDefaultInstance(), + InternalTransaction.TrxType.TRX_UNKNOWN_TYPE)); + + VMConfig.initAllowEnergyAdjustment(1); + VMConfig.initAllowTvmSelfdestructRestriction(1); + VMConfig.initAllowTvmFreeze(1); + VMConfig.initAllowTvmFreezeV2(1); + VMConfig.initAllowTvmCompatibleEvm(1); + VMConfig.initAllowTvmVote(1); + byte prePrefixByte = DecodeUtil.addressPreFixByte; + DecodeUtil.addressPreFixByte = Constant.ADD_PRE_FIX_BYTE_MAINNET; + + program.stackPush(new DataWord( + dbManager.getAccountStore().getBlackhole().getAddress().toByteArray())); + OperationActions.suicideAction2(program); + + Assert.assertEquals(1, program.getResult().getDeleteAccounts().size()); + + + invoke = new ProgramInvokeMockImpl(StoreFactory.getInstance(), new byte[0], contractAddr); + program = new Program(null, null, invoke, + new InternalTransaction( + Protocol.Transaction.getDefaultInstance(), + InternalTransaction.TrxType.TRX_UNKNOWN_TYPE)); + Program spyProgram = Mockito.spy(program); + Repository realContractState = program.getContractState(); + Repository spyContractState = Mockito.spy(realContractState); + Mockito.when(spyContractState.isNewContract(any(byte[].class))).thenReturn(false); + Mockito.when(spyProgram.getContractState()).thenReturn(spyContractState); + spyProgram.suicide2(new DataWord( + dbManager.getAccountStore().getBlackhole().getAddress().toByteArray())); + + Assert.assertEquals(0, spyProgram.getResult().getDeleteAccounts().size()); + + DecodeUtil.addressPreFixByte = prePrefixByte; + VMConfig.initAllowEnergyAdjustment(0); + VMConfig.initAllowTvmSelfdestructRestriction(0); + VMConfig.initAllowTvmFreeze(0); + VMConfig.initAllowTvmFreezeV2(0); + VMConfig.initAllowTvmCompatibleEvm(0); + VMConfig.initAllowTvmVote(0); + } + @Test public void testVoteWitnessCost() throws ContractValidateException { // Build stack environment, the stack from top to bottom is 0x00, 0x80, 0x00, 0x80 diff --git a/framework/src/test/java/org/tron/core/actuator/utils/ProposalUtilTest.java b/framework/src/test/java/org/tron/core/actuator/utils/ProposalUtilTest.java index e8a1e862f54..d83f7f14e29 100644 --- a/framework/src/test/java/org/tron/core/actuator/utils/ProposalUtilTest.java +++ b/framework/src/test/java/org/tron/core/actuator/utils/ProposalUtilTest.java @@ -439,6 +439,8 @@ public void validateCheck() { testAllowTvmBlobProposal(); + testAllowTvmSelfdestructRestrictionProposal(); + forkUtils.getManager().getDynamicPropertiesStore() .statsByVersion(ForkBlockVersionEnum.ENERGY_LIMIT.getValue(), stats); forkUtils.reset(); @@ -659,6 +661,58 @@ private void testAllowTvmBlobProposal() { } + private void testAllowTvmSelfdestructRestrictionProposal() { + byte[] stats = new byte[27]; + forkUtils.getManager().getDynamicPropertiesStore() + .statsByVersion(ForkBlockVersionEnum.VERSION_4_8_1.getValue(), stats); + try { + ProposalUtil.validator(dynamicPropertiesStore, forkUtils, + ProposalType.ALLOW_TVM_SELFDESTRUCT_RESTRICTION.getCode(), 1); + Assert.fail(); + } catch (ContractValidateException e) { + Assert.assertEquals( + "Bad chain parameter id [ALLOW_TVM_SELFDESTRUCT_RESTRICTION]", + e.getMessage()); + } + + long maintenanceTimeInterval = forkUtils.getManager().getDynamicPropertiesStore() + .getMaintenanceTimeInterval(); + + long hardForkTime = + ((ForkBlockVersionEnum.VERSION_4_8_1.getHardForkTime() - 1) / maintenanceTimeInterval + 1) + * maintenanceTimeInterval; + forkUtils.getManager().getDynamicPropertiesStore() + .saveLatestBlockHeaderTimestamp(hardForkTime + 1); + + stats = new byte[27]; + Arrays.fill(stats, (byte) 1); + forkUtils.getManager().getDynamicPropertiesStore() + .statsByVersion(ForkBlockVersionEnum.VERSION_4_8_1.getValue(), stats); + + // Should fail because the proposal value is invalid + try { + ProposalUtil.validator(dynamicPropertiesStore, forkUtils, + ProposalType.ALLOW_TVM_SELFDESTRUCT_RESTRICTION.getCode(), 2); + Assert.fail(); + } catch (ContractValidateException e) { + Assert.assertEquals( + "This value[ALLOW_TVM_SELFDESTRUCT_RESTRICTION] is only allowed to be 1", + e.getMessage()); + } + + dynamicPropertiesStore.saveAllowTvmSelfdestructRestriction(1); + try { + ProposalUtil.validator(dynamicPropertiesStore, forkUtils, + ProposalType.ALLOW_TVM_SELFDESTRUCT_RESTRICTION.getCode(), 1); + Assert.fail(); + } catch (ContractValidateException e) { + Assert.assertEquals( + "[ALLOW_TVM_SELFDESTRUCT_RESTRICTION] has been valid, no need to propose again", + e.getMessage()); + } + + } + @Test public void blockVersionCheck() { for (ForkBlockVersionEnum forkVersion : ForkBlockVersionEnum.values()) { From f22c8aee5a923ea8831b17370fc796d3293231e6 Mon Sep 17 00:00:00 2001 From: zeusoo001 <161573193+zeusoo001@users.noreply.github.com> Date: Thu, 21 Aug 2025 18:11:20 +0800 Subject: [PATCH 38/59] feat(net): optimize fetch inventory message processing logic (#5895) * feat(net): optimize fetch inventory message broadcast processing logic * feat(net): solve checkstyle problem * add the missing code in the code conflict --- .../FetchInvDataMsgHandler.java | 105 ++++++++++-------- .../FetchInvDataMsgHandlerTest.java | 25 +++++ 2 files changed, 86 insertions(+), 44 deletions(-) diff --git a/framework/src/main/java/org/tron/core/net/messagehandler/FetchInvDataMsgHandler.java b/framework/src/main/java/org/tron/core/net/messagehandler/FetchInvDataMsgHandler.java index 38cf3f2a0e2..ecb7853ce6f 100644 --- a/framework/src/main/java/org/tron/core/net/messagehandler/FetchInvDataMsgHandler.java +++ b/framework/src/main/java/org/tron/core/net/messagehandler/FetchInvDataMsgHandler.java @@ -38,7 +38,7 @@ public class FetchInvDataMsgHandler implements TronMsgHandler { private volatile Cache epochCache = CacheBuilder.newBuilder().initialCapacity(100) - .maximumSize(1000).expireAfterWrite(1, TimeUnit.HOURS).build(); + .maximumSize(1000).expireAfterWrite(1, TimeUnit.HOURS).build(); private static final int MAX_SIZE = 1_000_000; @Autowired @@ -55,7 +55,9 @@ public void processMessage(PeerConnection peer, TronMessage msg) throws P2pExcep FetchInvDataMessage fetchInvDataMsg = (FetchInvDataMessage) msg; - check(peer, fetchInvDataMsg); + boolean isAdv = isAdvInv(peer, fetchInvDataMsg); + + check(peer, fetchInvDataMsg, isAdv); InventoryType type = fetchInvDataMsg.getInventoryType(); List transactions = Lists.newArrayList(); @@ -64,6 +66,15 @@ public void processMessage(PeerConnection peer, TronMessage msg) throws P2pExcep for (Sha256Hash hash : fetchInvDataMsg.getHashList()) { Item item = new Item(hash, type); + /* Cache the Inventory sent to the peer. + Once a FetchInvData message is received from the peer, remove this Inventory from the cache. + If the same FetchInvData request is received from the peer again and it is + no longer in the cache, then reject the request. + * */ + if (isAdv) { + peer.getAdvInvSpread().invalidate(item); + } + Message message = advService.getMessage(item); if (message == null) { try { @@ -84,7 +95,7 @@ public void processMessage(PeerConnection peer, TronMessage msg) throws P2pExcep } else { transactions.add(((TransactionMessage) message).getTransactionCapsule().getInstance()); size += ((TransactionMessage) message).getTransactionCapsule().getInstance() - .getSerializedSize(); + .getSerializedSize(); if (size > MAX_SIZE) { peer.sendMessage(new TransactionsMessage(transactions)); transactions = Lists.newArrayList(); @@ -104,16 +115,16 @@ private void sendPbftCommitMessage(PeerConnection peer, BlockCapsule blockCapsul } long epoch = 0; PbftSignCapsule pbftSignCapsule = tronNetDelegate - .getBlockPbftCommitData(blockCapsule.getNum()); + .getBlockPbftCommitData(blockCapsule.getNum()); long maintenanceTimeInterval = consensusDelegate.getDynamicPropertiesStore() - .getMaintenanceTimeInterval(); + .getMaintenanceTimeInterval(); if (pbftSignCapsule != null) { Raw raw = Raw.parseFrom(pbftSignCapsule.getPbftCommitResult().getData()); epoch = raw.getEpoch(); peer.sendMessage(new PbftCommitMessage(pbftSignCapsule)); } else { - epoch = - (blockCapsule.getTimeStamp() / maintenanceTimeInterval + 1) * maintenanceTimeInterval; + epoch = (blockCapsule.getTimeStamp() / maintenanceTimeInterval + 1) + * maintenanceTimeInterval; } if (epochCache.getIfPresent(epoch) == null) { PbftSignCapsule srl = tronNetDelegate.getSRLPbftCommitData(epoch); @@ -127,7 +138,21 @@ private void sendPbftCommitMessage(PeerConnection peer, BlockCapsule blockCapsul } } - private void check(PeerConnection peer, FetchInvDataMessage fetchInvDataMsg) throws P2pException { + public boolean isAdvInv(PeerConnection peer, FetchInvDataMessage msg) { + MessageTypes type = msg.getInvMessageType(); + if (type == MessageTypes.TRX) { + return true; + } + for (Sha256Hash hash : msg.getHashList()) { + if (peer.getAdvInvSpread().getIfPresent(new Item(hash, InventoryType.BLOCK)) == null) { + return false; + } + } + return true; + } + + private void check(PeerConnection peer, FetchInvDataMessage fetchInvDataMsg, + boolean isAdv) throws P2pException { MessageTypes type = fetchInvDataMsg.getInvMessageType(); if (type == MessageTypes.TRX) { @@ -144,46 +169,38 @@ private void check(PeerConnection peer, FetchInvDataMessage fetchInvDataMsg) thr + "maxCount: {}, fetchCount: {}, peer: {}", maxCount, fetchCount, peer.getInetAddress()); } - } else { - boolean isAdv = true; - for (Sha256Hash hash : fetchInvDataMsg.getHashList()) { - if (peer.getAdvInvSpread().getIfPresent(new Item(hash, InventoryType.BLOCK)) == null) { - isAdv = false; - break; - } + } + + if (!isAdv) { + if (!peer.isNeedSyncFromUs()) { + throw new P2pException(TypeEnum.BAD_MESSAGE, "no need sync"); } - if (!isAdv) { - if (!peer.isNeedSyncFromUs()) { - throw new P2pException(TypeEnum.BAD_MESSAGE, "no need sync"); - } - if (!peer.getP2pRateLimiter().tryAcquire(fetchInvDataMsg.getType().asByte())) { - throw new P2pException(TypeEnum.RATE_LIMIT_EXCEEDED, fetchInvDataMsg.getType() - + " message exceeds the rate limit"); + if (!peer.getP2pRateLimiter().tryAcquire(fetchInvDataMsg.getType().asByte())) { + throw new P2pException(TypeEnum.RATE_LIMIT_EXCEEDED, fetchInvDataMsg.getType() + + " message exceeds the rate limit"); + } + if (fetchInvDataMsg.getHashList().size() > NetConstants.MAX_BLOCK_FETCH_PER_PEER) { + throw new P2pException(TypeEnum.BAD_MESSAGE, "fetch too many blocks, size:" + + fetchInvDataMsg.getHashList().size()); + } + for (Sha256Hash hash : fetchInvDataMsg.getHashList()) { + long blockNum = new BlockId(hash).getNum(); + long minBlockNum = + peer.getLastSyncBlockId().getNum() - 2 * NetConstants.SYNC_FETCH_BATCH_NUM; + if (blockNum < minBlockNum) { + throw new P2pException(TypeEnum.BAD_MESSAGE, + "minBlockNum: " + minBlockNum + ", blockNum: " + blockNum); } - if (fetchInvDataMsg.getHashList().size() > NetConstants.MAX_BLOCK_FETCH_PER_PEER) { - throw new P2pException(TypeEnum.BAD_MESSAGE, "fetch too many blocks, size:" - + fetchInvDataMsg.getHashList().size()); + if (blockNum > peer.getLastSyncBlockId().getNum()) { + throw new P2pException(TypeEnum.BAD_MESSAGE, + "maxBlockNum: " + peer.getLastSyncBlockId().getNum() + ", blockNum: " + blockNum); } - for (Sha256Hash hash : fetchInvDataMsg.getHashList()) { - long blockNum = new BlockId(hash).getNum(); - long minBlockNum = - peer.getLastSyncBlockId().getNum() - 2 * NetConstants.SYNC_FETCH_BATCH_NUM; - if (blockNum < minBlockNum) { - throw new P2pException(TypeEnum.BAD_MESSAGE, - "minBlockNum: " + minBlockNum + ", blockNum: " + blockNum); - } - if (blockNum > peer.getLastSyncBlockId().getNum()) { - throw new P2pException(TypeEnum.BAD_MESSAGE, - "maxBlockNum: " + peer.getLastSyncBlockId().getNum() + ", blockNum: " + blockNum); - } - if (peer.getSyncBlockIdCache().getIfPresent(hash) != null) { - throw new P2pException(TypeEnum.BAD_MESSAGE, - new BlockId(hash).getString() + " is exist"); - } - peer.getSyncBlockIdCache().put(hash, System.currentTimeMillis()); + if (peer.getSyncBlockIdCache().getIfPresent(hash) != null) { + throw new P2pException(TypeEnum.BAD_MESSAGE, + new BlockId(hash).getString() + " is exist"); } + peer.getSyncBlockIdCache().put(hash, System.currentTimeMillis()); } } } - -} +} \ No newline at end of file diff --git a/framework/src/test/java/org/tron/core/net/messagehandler/FetchInvDataMsgHandlerTest.java b/framework/src/test/java/org/tron/core/net/messagehandler/FetchInvDataMsgHandlerTest.java index 43036ce142a..270002fffba 100644 --- a/framework/src/test/java/org/tron/core/net/messagehandler/FetchInvDataMsgHandlerTest.java +++ b/framework/src/test/java/org/tron/core/net/messagehandler/FetchInvDataMsgHandlerTest.java @@ -67,6 +67,31 @@ public void testProcessMessage() throws Exception { Assert.assertNotNull(syncBlockIdCache.getIfPresent(blockId)); } + @Test + public void testIsAdvInv() { + FetchInvDataMsgHandler fetchInvDataMsgHandler = new FetchInvDataMsgHandler(); + + List list = new LinkedList<>(); + list.add(Sha256Hash.ZERO_HASH); + FetchInvDataMessage msg = + new FetchInvDataMessage(list, Protocol.Inventory.InventoryType.TRX); + + boolean isAdv = fetchInvDataMsgHandler.isAdvInv(null, msg); + Assert.assertTrue(isAdv); + + PeerConnection peer = Mockito.mock(PeerConnection.class); + Cache advInvSpread = CacheBuilder.newBuilder().build(); + Mockito.when(peer.getAdvInvSpread()).thenReturn(advInvSpread); + + msg = new FetchInvDataMessage(list, Protocol.Inventory.InventoryType.BLOCK); + isAdv = fetchInvDataMsgHandler.isAdvInv(peer, msg); + Assert.assertTrue(!isAdv); + + advInvSpread.put(new Item(Sha256Hash.ZERO_HASH, Protocol.Inventory.InventoryType.BLOCK), 1L); + isAdv = fetchInvDataMsgHandler.isAdvInv(peer, msg); + Assert.assertTrue(isAdv); + } + @Test public void testSyncFetchCheck() { BlockCapsule.BlockId blockId = new BlockCapsule.BlockId(Sha256Hash.ZERO_HASH, 10000L); From 8481e812c1f40a0f3abb0cbe1ab925948c562a6c Mon Sep 17 00:00:00 2001 From: halibobo1205 <82020050+halibobo1205@users.noreply.github.com> Date: Tue, 26 Aug 2025 14:45:16 +0800 Subject: [PATCH 39/59] feat(dependencies): update grpc (#6429) avoid CVE-2025-55163,MadeYouReset 1. bump grpc-java from 1.60.0 to 1.75.0 2. bump protobuf from 3.25.5 to 3.25.8 3. add node.rpc.maxRstStream and node.rpc.secondsPerWindow 4. bump libp2p to 2.2.7-SNAPSHOT --- build.gradle | 15 + common/build.gradle | 2 +- .../common/parameter/CommonParameter.java | 9 + .../src/main/java/org/tron/core/Constant.java | 2 + .../tron/common/application/RpcService.java | 4 + .../java/org/tron/core/config/args/Args.java | 8 + .../java/org/tron/common/ParameterTest.java | 6 + .../core/services/RpcApiServicesTest.java | 3 + framework/src/test/resources/config-test.conf | 2 + gradle/verification-metadata.xml | 423 ++++++++++-------- protocol/build.gradle | 8 +- 11 files changed, 285 insertions(+), 197 deletions(-) diff --git a/build.gradle b/build.gradle index c03308d6fca..000cc05479b 100644 --- a/build.gradle +++ b/build.gradle @@ -126,6 +126,21 @@ subprojects { } } } + configurations.configureEach { + resolutionStrategy { + eachDependency { details -> + if (details.requested.group == 'com.google.guava' && + details.requested.name == 'guava') { + def requestedVersion = details.requested.version + if (requestedVersion.matches(/.*-android$/)) { + def jreVersion = requestedVersion.replaceAll(/-android$/, '-jre') + details.useVersion(jreVersion) + details.because("Automatically replace android guava with jre version: ${requestedVersion} -> ${jreVersion}") + } + } + } + } + } } task copyToParent(type: Copy) { diff --git a/common/build.gradle b/common/build.gradle index 83fe638ae50..c721fc699a7 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -22,7 +22,7 @@ dependencies { api 'org.aspectj:aspectjrt:1.9.8' api 'org.aspectj:aspectjweaver:1.9.8' api 'org.aspectj:aspectjtools:1.9.8' - api group: 'io.github.tronprotocol', name: 'libp2p', version: '2.2.6',{ + api group: 'com.github.tronprotocol', name: 'libp2p', version: 'release-v2.2.7-SNAPSHOT',{ exclude group: 'io.grpc', module: 'grpc-context' exclude group: 'io.grpc', module: 'grpc-core' exclude group: 'io.grpc', module: 'grpc-netty' diff --git a/common/src/main/java/org/tron/common/parameter/CommonParameter.java b/common/src/main/java/org/tron/common/parameter/CommonParameter.java index 9e8c3682e66..c70775cc8f4 100644 --- a/common/src/main/java/org/tron/common/parameter/CommonParameter.java +++ b/common/src/main/java/org/tron/common/parameter/CommonParameter.java @@ -249,6 +249,15 @@ public class CommonParameter { @Getter @Setter public int flowControlWindow; + // the positive limit of RST_STREAM frames per connection per period for grpc, + // 0 or Integer.MAX_VALUE for unlimited, by default there is no limit. + @Getter + @Setter + public int rpcMaxRstStream; + // the positive number of seconds per period for grpc + @Getter + @Setter + public int rpcSecondsPerWindow; @Getter @Setter public long maxConnectionIdleInMillis; diff --git a/common/src/main/java/org/tron/core/Constant.java b/common/src/main/java/org/tron/core/Constant.java index 3d5c56dbbf0..5b0488561a5 100644 --- a/common/src/main/java/org/tron/core/Constant.java +++ b/common/src/main/java/org/tron/core/Constant.java @@ -163,6 +163,8 @@ public class Constant { public static final String NODE_RPC_MAX_CONCURRENT_CALLS_PER_CONNECTION = "node.rpc.maxConcurrentCallsPerConnection"; public static final String NODE_RPC_FLOW_CONTROL_WINDOW = "node.rpc.flowControlWindow"; public static final String NODE_RPC_MAX_CONNECTION_IDLE_IN_MILLIS = "node.rpc.maxConnectionIdleInMillis"; + public static final String NODE_RPC_MAX_RST_STREAM = "node.rpc.maxRstStream"; + public static final String NODE_RPC_SECONDS_PER_WINDOW = "node.rpc.secondsPerWindow"; public static final String NODE_PRODUCED_TIMEOUT = "node.blockProducedTimeOut"; public static final String NODE_MAX_HTTP_CONNECT_NUMBER = "node.maxHttpConnectNumber"; diff --git a/framework/src/main/java/org/tron/common/application/RpcService.java b/framework/src/main/java/org/tron/common/application/RpcService.java index 2d118806e2c..4a7b3fa0f98 100644 --- a/framework/src/main/java/org/tron/common/application/RpcService.java +++ b/framework/src/main/java/org/tron/common/application/RpcService.java @@ -88,6 +88,10 @@ protected NettyServerBuilder initServerBuilder() { .maxConnectionAge(parameter.getMaxConnectionAgeInMillis(), TimeUnit.MILLISECONDS) .maxInboundMessageSize(parameter.getMaxMessageSize()) .maxHeaderListSize(parameter.getMaxHeaderListSize()); + if (parameter.getRpcMaxRstStream() > 0 && parameter.getRpcSecondsPerWindow() > 0) { + serverBuilder.maxRstFramesPerWindow( + parameter.getRpcMaxRstStream(), parameter.getRpcSecondsPerWindow()); + } if (parameter.isRpcReflectionServiceEnable()) { serverBuilder.addService(ProtoReflectionService.newInstance()); diff --git a/framework/src/main/java/org/tron/core/config/args/Args.java b/framework/src/main/java/org/tron/core/config/args/Args.java index d2b8b00926a..4583db56d97 100644 --- a/framework/src/main/java/org/tron/core/config/args/Args.java +++ b/framework/src/main/java/org/tron/core/config/args/Args.java @@ -251,6 +251,8 @@ public static void clearParam() { PARAMETER.allowTvmCancun = 0; PARAMETER.allowTvmBlob = 0; PARAMETER.allowTvmSelfdestructRestriction = 0; + PARAMETER.rpcMaxRstStream = 0; + PARAMETER.rpcSecondsPerWindow = 0; } /** @@ -723,6 +725,12 @@ public static void setParam(final Config config) { PARAMETER.flowControlWindow = config.hasPath(Constant.NODE_RPC_FLOW_CONTROL_WINDOW) ? config.getInt(Constant.NODE_RPC_FLOW_CONTROL_WINDOW) : NettyServerBuilder.DEFAULT_FLOW_CONTROL_WINDOW; + if (config.hasPath(Constant.NODE_RPC_MAX_RST_STREAM)) { + PARAMETER.rpcMaxRstStream = config.getInt(Constant.NODE_RPC_MAX_RST_STREAM); + } + if (config.hasPath(Constant.NODE_RPC_SECONDS_PER_WINDOW)) { + PARAMETER.rpcSecondsPerWindow = config.getInt(Constant.NODE_RPC_SECONDS_PER_WINDOW); + } PARAMETER.maxConnectionIdleInMillis = config.hasPath(Constant.NODE_RPC_MAX_CONNECTION_IDLE_IN_MILLIS) diff --git a/framework/src/test/java/org/tron/common/ParameterTest.java b/framework/src/test/java/org/tron/common/ParameterTest.java index 03fdf462b26..6724ced20b5 100644 --- a/framework/src/test/java/org/tron/common/ParameterTest.java +++ b/framework/src/test/java/org/tron/common/ParameterTest.java @@ -129,6 +129,12 @@ public void testCommonParameter() { assertEquals(10, parameter.getMaxConcurrentCallsPerConnection()); parameter.setFlowControlWindow(20); assertEquals(20, parameter.getFlowControlWindow()); + assertEquals(0, parameter.getRpcMaxRstStream()); + parameter.setRpcMaxRstStream(10); + assertEquals(10, parameter.getRpcMaxRstStream()); + assertEquals(0, parameter.getRpcSecondsPerWindow()); + parameter.setRpcSecondsPerWindow(5); + assertEquals(5, parameter.getRpcSecondsPerWindow()); parameter.setMaxConnectionIdleInMillis(1000); assertEquals(1000, parameter.getMaxConnectionIdleInMillis()); parameter.setBlockProducedTimeOut(500); diff --git a/framework/src/test/java/org/tron/core/services/RpcApiServicesTest.java b/framework/src/test/java/org/tron/core/services/RpcApiServicesTest.java index 48a25b141dc..d5bad3f802c 100644 --- a/framework/src/test/java/org/tron/core/services/RpcApiServicesTest.java +++ b/framework/src/test/java/org/tron/core/services/RpcApiServicesTest.java @@ -12,6 +12,7 @@ import java.io.IOException; import java.util.Objects; import org.junit.AfterClass; +import org.junit.Assert; import org.junit.BeforeClass; import org.junit.ClassRule; import org.junit.FixMethodOrder; @@ -131,6 +132,8 @@ public class RpcApiServicesTest { @BeforeClass public static void init() throws IOException { Args.setParam(new String[]{"-d", temporaryFolder.newFolder().toString()}, Constant.TEST_CONF); + Assert.assertEquals(5, getInstance().getRpcMaxRstStream()); + Assert.assertEquals(10, getInstance().getRpcSecondsPerWindow()); String OWNER_ADDRESS = Wallet.getAddressPreFixString() + "548794500882809695a8a687866e76d4271a1abc"; getInstance().setRpcEnable(true); diff --git a/framework/src/test/resources/config-test.conf b/framework/src/test/resources/config-test.conf index f7884eec831..673d4932601 100644 --- a/framework/src/test/resources/config-test.conf +++ b/framework/src/test/resources/config-test.conf @@ -209,6 +209,8 @@ node { # The switch of the reflection service, effective for all gRPC services reflectionService = true + maxRstStream = 5 + secondsPerWindow = 10 } } diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index e8f2596e195..ffd5a8d9b6d 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -6,6 +6,7 @@ + @@ -291,12 +292,12 @@ - - - + + + - - + + @@ -323,12 +324,12 @@ - - - + + + - - + + @@ -339,9 +340,9 @@ - - - + + + @@ -362,12 +363,12 @@ - - - + + + - - + + @@ -380,9 +381,9 @@ - - - + + + @@ -393,12 +394,12 @@ - - - + + + - - + + @@ -432,6 +433,28 @@ + + + + + + + + + + + + + + + + + + + + + + @@ -462,6 +485,16 @@ + + + + + + + + + + @@ -479,16 +512,21 @@ - - - - - - + + + + + + + + + + + @@ -499,43 +537,42 @@ - - - + + + - - + + - - - + + + - - + + - - - + + + - - - - + + + - - + + - - + + - - + + - - + + @@ -828,76 +865,76 @@ - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + @@ -918,108 +955,108 @@ - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - + + @@ -1088,14 +1125,14 @@ - - - - - - - - + + + + + + + + @@ -1590,15 +1627,15 @@ - - - + + + - - + + - - + + @@ -1614,12 +1651,12 @@ - - - + + + - - + + @@ -1627,9 +1664,9 @@ - - - + + + @@ -1637,9 +1674,9 @@ - - - + + + diff --git a/protocol/build.gradle b/protocol/build.gradle index 535fac43a65..152a3af098a 100644 --- a/protocol/build.gradle +++ b/protocol/build.gradle @@ -1,7 +1,8 @@ apply plugin: 'com.google.protobuf' -def protobufVersion = '3.25.5' -def grpcVersion = '1.60.0' +def protobufVersion = '3.25.8' +def grpcVersion = '1.75.0' +def protocGenVersion = '1.60.0' // https://github.com/grpc/grpc-java/pull/11371 , 1.64.x is not supported CentOS 7. dependencies { api group: 'com.google.protobuf', name: 'protobuf-java', version: protobufVersion @@ -13,6 +14,7 @@ dependencies { api group: 'io.grpc', name: 'grpc-netty', version: grpcVersion api group: 'io.grpc', name: 'grpc-protobuf', version: grpcVersion api group: 'io.grpc', name: 'grpc-stub', version: grpcVersion + api group: 'io.grpc', name: 'grpc-core', version: grpcVersion api group: 'io.grpc', name: 'grpc-services', version: grpcVersion // end google grpc @@ -45,7 +47,7 @@ protobuf { plugins { grpc { - artifact = "io.grpc:protoc-gen-grpc-java:${grpcVersion}" + artifact = "io.grpc:protoc-gen-grpc-java:${protocGenVersion}" } } generateProtoTasks { From 6d4e244c76f9e289b22e562a4068f23821d6832c Mon Sep 17 00:00:00 2001 From: halibobo1205 <82020050+halibobo1205@users.noreply.github.com> Date: Tue, 26 Aug 2025 18:53:50 +0800 Subject: [PATCH 40/59] feat(gradle): update verification metadata (#6431) --- README.md | 3 +++ gradle/verification-metadata.xml | 27 ++++++++++++++++++++++++++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 3a21f3d63f4..9f394ec3a64 100644 --- a/README.md +++ b/README.md @@ -85,6 +85,9 @@ then run the following command to build java-tron, the `FullNode.jar` file can b ```bash $ ./gradlew clean build -x test + # To fix DependencyVerificationException: Dependency verification failed for configuration ':xxx' x artifacts failed verification +$ ./gradlew clean --refresh-dependencies --write-verification-metadata sha256 + ``` # Running java-tron diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index ffd5a8d9b6d..baad667c071 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -938,7 +938,6 @@ - @@ -1337,6 +1336,11 @@ + + + + + @@ -1371,6 +1375,14 @@ + + + + + + + + @@ -1437,6 +1449,11 @@ + + + + + @@ -1988,6 +2005,14 @@ + + + + + + + + From b1bf6aa6697033044bfa850cec334409e671adfb Mon Sep 17 00:00:00 2001 From: 317787106 <317787106@qq.com> Date: Fri, 29 Aug 2025 15:21:39 +0800 Subject: [PATCH 41/59] feat(config): fill whole config items (#6430) * fill whole config item * update config.conf * update comment of committee * update comment of storageEnable * add node.rpc.maxRstStream, node.rpc.secondsPerWindow * delete item actuator.whitelist * comments of single line should starts with # * add two items * add config item node.maxTps * add some comments --- README.md | 2 +- framework/src/main/resources/config.conf | 335 ++++++++++++++--------- 2 files changed, 200 insertions(+), 137 deletions(-) diff --git a/README.md b/README.md index 9f394ec3a64..4a40bb7dff5 100644 --- a/README.md +++ b/README.md @@ -105,7 +105,7 @@ Requires 64-bit version of `JDK 17` to be installed, other JDK versions are not ## Configuration flile -Get the mainnet configuration file: [main_net_config.conf](https://github.com/tronprotocol/tron-deployment/blob/master/main_net_config.conf), other network configuration files can be found [here](https://github.com/tronprotocol/tron-deployment). +Get the mainnet configuration file: [config.conf](framework/src/main/resources/config.conf), other network configuration files can be found [here](https://github.com/tronprotocol/tron-deployment). ## Hardware Requirements diff --git a/framework/src/main/resources/config.conf b/framework/src/main/resources/config.conf index 55e4b8c55d9..6fc5fc91cea 100644 --- a/framework/src/main/resources/config.conf +++ b/framework/src/main/resources/config.conf @@ -1,6 +1,8 @@ net { + # Type can be 'mainnet' or 'testnet', refers to address type. + # Hex address of 'mainnet' begin with 0x41, and 'testnet' begin with 0xa0. + # Note: 'testnet' is not related to TRON network Nile, Shasta or private net type = mainnet - # type = testnet } storage { @@ -8,83 +10,69 @@ storage { db.engine = "LEVELDB", // deprecated for arm, because arm only support "ROCKSDB". db.sync = false, db.directory = "database", - index.directory = "index", - transHistory.switch = "on", - # You can custom these 14 databases' configs: - - # account, account-index, asset-issue, block, block-index, - # block_KDB, peers, properties, recent-block, trans, - # utxo, votes, witness, witness_schedule. - # Otherwise, db configs will remain default and data will be stored in - # the path of "output-directory" or which is set by "-d" ("--output-directory"). + # Whether to write transaction result in transactionRetStore + transHistory.switch = "on", - # setting can impove leveldb performance .... start, deprecated for arm + # setting can improve leveldb performance .... start, deprecated for arm # node: if this will increase process fds,you may be check your ulimit if 'too many open files' error occurs # see https://github.com/tronprotocol/tips/blob/master/tip-343.md for detail - # if you find block sync has lower performance,you can try this settings - #default = { + # if you find block sync has lower performance, you can try this settings + # default = { # maxOpenFiles = 100 - #} - #defaultM = { + # } + # defaultM = { # maxOpenFiles = 500 - #} - #defaultL = { + # } + # defaultL = { # maxOpenFiles = 1000 - #} - # setting can impove leveldb performance .... end, deprecated for arm + # } + # setting can improve leveldb performance .... end, deprecated for arm - # Attention: name is a required field that must be set !!! + # You can customize the configuration for each database. Otherwise, the database settings will use + # their defaults, and data will be stored in the "output-directory" or in the directory specified + # by the "-d" or "--output-directory" option. Attention: name is a required field that must be set! properties = [ - // { - // name = "account", - // path = "storage_directory_test", - // createIfMissing = true, // deprecated for arm start - // paranoidChecks = true, - // verifyChecksums = true, - // compressionType = 1, // compressed with snappy - // blockSize = 4096, // 4 KB = 4 * 1024 B - // writeBufferSize = 10485760, // 10 MB = 10 * 1024 * 1024 B - // cacheSize = 10485760, // 10 MB = 10 * 1024 * 1024 B - // maxOpenFiles = 100 // deprecated for arm end - // }, - // { - // name = "account-index", - // path = "storage_directory_test", - // createIfMissing = true, - // paranoidChecks = true, - // verifyChecksums = true, - // compressionType = 1, // compressed with snappy - // blockSize = 4096, // 4 KB = 4 * 1024 B - // writeBufferSize = 10485760, // 10 MB = 10 * 1024 * 1024 B - // cacheSize = 10485760, // 10 MB = 10 * 1024 * 1024 B - // maxOpenFiles = 100 - // }, + # { + # name = "account", + # path = "storage_directory_test", + # createIfMissing = true, // deprecated for arm start + # paranoidChecks = true, + # verifyChecksums = true, + # compressionType = 1, // compressed with snappy + # blockSize = 4096, // 4 KB = 4 * 1024 B + # writeBufferSize = 10485760, // 10 MB = 10 * 1024 * 1024 B + # cacheSize = 10485760, // 10 MB = 10 * 1024 * 1024 B + # maxOpenFiles = 100 // deprecated for arm end + # }, + # { + # name = "account-index", + # path = "storage_directory_test", + # createIfMissing = true, + # paranoidChecks = true, + # verifyChecksums = true, + # compressionType = 1, // compressed with snappy + # blockSize = 4096, // 4 KB = 4 * 1024 B + # writeBufferSize = 10485760, // 10 MB = 10 * 1024 * 1024 B + # cacheSize = 10485760, // 10 MB = 10 * 1024 * 1024 B + # maxOpenFiles = 100 + # }, ] needToUpdateAsset = true - //dbsettings is needed when using rocksdb as the storage implement (db.engine="ROCKSDB"). - //we'd strongly recommend that do not modify it unless you know every item's meaning clearly. + # dbsettings is needed when using rocksdb as the storage implement (db.engine="ROCKSDB"). + # we'd strongly recommend that do not modify it unless you know every item's meaning clearly. dbSettings = { levelNumber = 7 - //compactThreads = 32 + # compactThreads = 32 blocksize = 64 // n * KB maxBytesForLevelBase = 256 // n * MB maxBytesForLevelMultiplier = 10 level0FileNumCompactionTrigger = 4 targetFileSizeBase = 256 // n * MB targetFileSizeMultiplier = 1 - } - - //backup settings when using rocks db as the storage implement (db.engine="ROCKSDB"). - //if you want to use the backup plugin, please confirm set the db.engine="ROCKSDB" above. - backup = { - enable = false // indicate whether enable the backup plugin - propPath = "prop.properties" // record which bak directory is valid - bak1path = "bak1/database" // you must set two backup directories to prevent application halt unexpected(e.g. kill -9). - bak2path = "bak2/database" - frequency = 10000 // indicate backup db once every 10000 blocks processed. + maxOpenFiles = 5000 } balance.history.lookup = false @@ -95,11 +83,14 @@ storage { # the estimated number of block transactions (default 1000, min 100, max 10000). # so the total number of cached transactions is 65536 * txCache.estimatedTransactions # txCache.estimatedTransactions = 1000 - # if true, transaction cache initialization will be faster. default false - # txCache.initOptimization = true - # data root setting, for check data, currently, only reward-vi is used. + # if true, transaction cache initialization will be faster. Default: false + txCache.initOptimization = true + # The number of blocks flushed to db in each batch during node syncing. Default: 1 + # snapshot.maxFlushCount = 1 + + # data root setting, for check data, currently, only reward-vi is used. # merkleRoot = { # reward-vi = 9debcb9924055500aaae98cdee10501c5c39d4daa75800a996f4bdda73dbccd8 // main-net, Sha256Hash, hexString # } @@ -135,18 +126,27 @@ node.backup { ] } +# Specify the algorithm for generating a public key from private key. To avoid forks, please do not modify it crypto { engine = "eckey" } -# prometheus metrics start -# node.metrics = { -# prometheus{ -# enable=true -# port="9527" -# } -# } -# prometheus metrics end +node.metrics = { + # prometheus metrics + prometheus { + enable = false + port = 9527 + } + + # influxdb metrics + storageEnable = false # Whether write metrics data into InfluxDb. Default: false. + influxdb { + ip = "" + port = 8086 + database = "" + metricsReportInterval = 10 + } +} node { # trust node for solidity node @@ -161,10 +161,7 @@ node { connection.timeout = 2 fetchBlock.timeout = 200 - - tcpNettyWorkThreadNum = 0 - - udpNettyWorkThreadNum = 1 + # syncFetchBatchNum = 2000 # Number of validate sign thread, default availableProcessors # validateSignThreadNum = 16 @@ -181,11 +178,23 @@ node { minParticipationRate = 15 + # allowShieldedTransactionApi = true + + # openPrintLog = true + + # If set to true, SR packs transactions into a block in descending order of fee; otherwise, it packs + # them based on their receive timestamp. Default: false + # openTransactionSort = false + + # The threshold for the number of broadcast transactions received from each peer every second, + # transactions exceeding this threshold will be discarded + # maxTps = 1000 + isOpenFullTcpDisconnect = false inactiveThreshold = 600 //seconds p2p { - version = 11111 # 11111: mainnet; 20180622: testnet + version = 11111 # Mainnet:11111; Nile:201910292; Shasta:1 } active = [ @@ -245,17 +254,23 @@ node { # The maximum size of header list allowed to be received, default 8192 # maxHeaderListSize = + # The number of RST_STREAM frames allowed to be sent per connection per period for grpc, by default there is no limit. + # maxRstStream = + + # The number of seconds per period for grpc + # secondsPerWindow = + # Transactions can only be broadcast if the number of effective connections is reached. minEffectiveConnection = 1 - # The switch of the reflection service, effective for all gRPC services - # reflectionService = true + # The switch of the reflection service, effective for all gRPC services, used for grpcurl tool. Default: false + reflectionService = false } # number of solidity thread in the FullNode. # If accessing solidity rpc and http interface timeout, could increase the number of threads, # The default value is the number of cpu cores of the machine. - #solidity.threads = 8 + # solidity.threads = 8 # Limits the maximum percentage (default 75%) of producing block interval # to provide sufficient time to perform other operations e.g. broadcast block @@ -264,21 +279,24 @@ node { # Limits the maximum number (default 700) of transaction from network layer # netMaxTrxPerSecond = 700 - # Whether to enable the node detection function, default false + # Whether to enable the node detection function. Default: false # nodeDetectEnable = false - # use your ipv6 address for node discovery and tcp connection, default false + # use your ipv6 address for node discovery and tcp connection. Default: false # enableIpv6 = false - # if your node's highest block num is below than all your pees', try to acquire new connection. default false + # if your node's highest block num is below than all your pees', try to acquire new connection. Default: false # effectiveCheckEnable = false # Dynamic loading configuration function, disabled by default - # dynamicConfig = { - # enable = false - # Configuration file change check interval, default is 600 seconds - # checkInterval = 600 - # } + dynamicConfig = { + # enable = false + # checkInterval = 600 // Check interval of Configuration file's change, default is 600 seconds + } + + # Whether to continue broadcast transactions after at least maxUnsolidifiedBlocks are not solidified. Default: false + # unsolidifiedBlockCheck = false + # maxUnsolidifiedBlocks = 54 dns { # dns urls to get nodes, url format tree://{pubkey}@{domain}, default empty @@ -286,7 +304,7 @@ node { #"tree://AKMQMNAJJBL73LXWPXDI4I5ZWWIZ4AWO34DWQ636QOBBXNFXH3LQS@main.trondisco.net", ] - # enable or disable dns publish, default false + # enable or disable dns publish. Default: false # publish = false # dns domain to publish nodes, required if publish is true @@ -332,22 +350,20 @@ node { # awsHostZoneId = "your-host-zone-id" } - # open the history query APIs(http&GRPC) when node is a lite fullNode, - # like {getBlockByNum, getBlockByID, getTransactionByID...}. - # default: false. + # open the history query APIs(http&GRPC) when node is a lite FullNode, + # like {getBlockByNum, getBlockByID, getTransactionByID...}. Default: false. # note: above APIs may return null even if blocks and transactions actually are on the blockchain - # when opening on a lite fullnode. only open it if the consequences being clearly known + # when opening on a lite FullNode. only open it if the consequences being clearly known # openHistoryQueryWhenLiteFN = false jsonrpc { - # Note: If you turn on jsonrpc and run it for a while and then turn it off, you will not - # be able to get the data from eth_getLogs for that period of time. - - # httpFullNodeEnable = true + # Note: Before release_4.8.1, if you turn on jsonrpc and run it for a while and then turn it off, + # you will not be able to get the data from eth_getLogs for that period of time. Default: false + # httpFullNodeEnable = false # httpFullNodePort = 8545 - # httpSolidityEnable = true + # httpSolidityEnable = false # httpSolidityPort = 8555 - # httpPBFTEnable = true + # httpPBFTEnable = false # httpPBFTPort = 8565 # The maximum blocks range to retrieve logs for eth_getLogs, default value is 5000, @@ -359,25 +375,22 @@ node { maxSubTopics = 1000 } - # Disabled api list, it will work for http, rpc and pbft, both fullnode and soliditynode, - # but not jsonrpc. - # Sample: The setting is case insensitive, GetNowBlock2 is equal to getnowblock2 - # - # disabledApi = [ - # "getaccount", - # "getnowblock2" - # ] + # Disabled api list, it will work for http, rpc and pbft, both FullNode and SolidityNode, + # but not jsonrpc. The setting is case insensitive, GetNowBlock2 is equal to getnowblock2 + disabledApi = [ + # "getaccount", + # "getnowblock2" + ] } ## rate limiter config rate.limiter = { - # Every api could be set a specific rate limit strategy. Three strategy are supported:GlobalPreemptibleAdapter、IPQPSRateLimiterAdapte、QpsRateLimiterAdapter - # GlobalPreemptibleAdapter: permit is the number of preemptible resource, every client must apply one resourse - # before do the request and release the resource after got the reponse automaticlly. permit should be a Integer. + # Every api could only set a specific rate limit strategy. Three blocking strategy are supported: + # GlobalPreemptibleAdapter: The number of preemptible resource or maximum concurrent requests globally. # QpsRateLimiterAdapter: qps is the average request count in one second supported by the server, it could be a Double or a Integer. # IPQPSRateLimiterAdapter: similar to the QpsRateLimiterAdapter, qps could be a Double or a Integer. - # If do not set, the "default strategy" is set.The "default startegy" is based on QpsRateLimiterAdapter, the qps is set as 1000. + # If not set, QpsRateLimiterAdapter with qps=1000 is the default strategy. # # Sample entries: # @@ -421,10 +434,16 @@ rate.limiter = { # }, ] + p2p = { + # syncBlockChain = 3.0 + # fetchInvData = 3.0 + # disconnect = 1.0 + } + # global qps, default 50000 - # global.qps = 50000 + global.qps = 50000 # IP-based global qps, default 10000 - # global.ip.qps = 10000 + global.ip.qps = 10000 } @@ -634,34 +653,33 @@ genesis.block = { } ] - timestamp = "0" #2017-8-26 12:00:00 + timestamp = "0" # Genesis block timestamp, milli seconds parentHash = "0xe58f33f9baf9305dc6f82b9f1934ea8f0ade2defb951258d50167028c780351f" } -// Optional.The default is empty. -// It is used when the witness account has set the witnessPermission. -// When it is not empty, the localWitnessAccountAddress represents the address of the witness account, -// and the localwitness is configured with the private key of the witnessPermissionAddress in the witness account. -// When it is empty,the localwitness is configured with the private key of the witness account. - -//localWitnessAccountAddress = +# Optional. The default is empty. It is used when the witness account has set the witnessPermission. +# When it is not empty, the localWitnessAccountAddress represents the address of the witness account, +# and the localwitness is configured with the private key of the witnessPermissionAddress in the witness account. +# When it is empty,the localwitness is configured with the private key of the witness account. +# localWitnessAccountAddress = localwitness = [ ] -#localwitnesskeystore = [ +# localwitnesskeystore = [ # "localwitnesskeystore.json" -#] +# ] block = { needSyncCheck = true - maintenanceTimeInterval = 21600000 - proposalExpireTime = 259200000 // 3 day: 259200000(ms) + maintenanceTimeInterval = 21600000 // 6 hours: 21600000(ms) + proposalExpireTime = 259200000 // 3 days: 259200000(ms) + # checkFrozenTime = 1 // for test only } # Transaction reference block, default is "solid", configure to "head" may cause TaPos error -# trx.reference.block = "solid" // head;solid; +trx.reference.block = "solid" // "head" or "solid" # This property sets the number of milliseconds after the creation of the transaction that is expired, default value is 60000. # trx.expiration.timeInMilliseconds = 60000 @@ -672,27 +690,73 @@ vm = { minTimeRatio = 0.0 maxTimeRatio = 5.0 saveInternalTx = false + # lruCacheSize = 500 + # vmTrace = false - # Indicates whether the node stores featured internal transactions, such as freeze, vote and so on + # Indicates whether the node stores featured internal transactions, such as freeze, vote and so on. Default: false. # saveFeaturedInternalTx = false - # Indicates whether the node stores the details of the internal transactions generated by the - # CANCELALLUNFREEZEV2 opcode, such as bandwidth/energy/tronpower cancel amount. + # Indicates whether the node stores the details of the internal transactions generated by the CANCELALLUNFREEZEV2 opcode, + # such as bandwidth/energy/tronpower cancel amount. Default: false. # saveCancelAllUnfreezeV2Details = false # In rare cases, transactions that will be within the specified maximum execution time (default 10(ms)) are re-executed and packaged # longRunningTime = 10 - # Indicates whether the node support estimate energy API. + # Indicates whether the node support estimate energy API. Default: false. # estimateEnergy = false - # Indicates the max retry time for executing transaction in estimating energy. + # Indicates the max retry time for executing transaction in estimating energy. Default 3. # estimateEnergyMaxRetry = 3 } +# These parameters are designed for private chain testing only and cannot be freely switched on or off in production systems. committee = { allowCreationOfContracts = 0 //mainnet:0 (reset by committee),test:1 allowAdaptiveEnergy = 0 //mainnet:0 (reset by committee),test:1 + # allowCreationOfContracts = 0 + # allowMultiSign = 0 + # allowAdaptiveEnergy = 0 + # allowDelegateResource = 0 + # allowSameTokenName = 0 + # allowTvmTransferTrc10 = 0 + # allowTvmConstantinople = 0 + # allowTvmSolidity059 = 0 + # forbidTransferToContract = 0 + # allowShieldedTRC20Transaction = 0 + # allowTvmIstanbul = 0 + # allowMarketTransaction = 0 + # allowProtoFilterNum = 0 + # allowAccountStateRoot = 0 + # changedDelegation = 0 + # allowPBFT = 0 + # pBFTExpireNum = 0 + # allowTransactionFeePool = 0 + # allowBlackHoleOptimization = 0 + # allowNewResourceModel = 0 + # allowReceiptsMerkleRoot = 0 + # allowTvmFreeze = 0 + # allowTvmVote = 0 + # unfreezeDelayDays = 0 + # allowTvmLondon = 0 + # allowTvmCompatibleEvm = 0 + # allowNewRewardAlgorithm = 0 + # allowAccountAssetOptimization = 0 + # allowAssetOptimization = 0 + # allowNewReward = 0 + # memoFee = 0 + # allowDelegateOptimization = 0 + # allowDynamicEnergy = 0 + # dynamicEnergyThreshold = 0 + # dynamicEnergyMaxFactor = 0 + # allowTvmShangHai = 0 + # allowOldRewardOpt = 0 + # allowEnergyAdjustment = 0 + # allowStrictMath = 0 + # allowTvmCancun = 0 + # allowTvmBlob = 0 + # consensusLogicOptimization = 0 + # allowOptimizedReturnValueOfChainId = 0 } event.subscribe = { @@ -708,10 +772,10 @@ event.subscribe = { path = "" // absolute path of plugin server = "" // target server address to receive event triggers - // dbname|username|password, if you want to create indexes for collections when the collections - // are not exist, you can add version and set it to 2, as dbname|username|password|version - // if you use version 2 and one collection not exists, it will create index automaticaly; - // if you use version 2 and one collection exists, it will not create index, you must create index manually; + # dbname|username|password, if you want to create indexes for collections when the collections + # are not exist, you can add version and set it to 2, as dbname|username|password|version + # if you use version 2 and one collection not exists, it will create index automaticaly; + # if you use version 2 and one collection exists, it will not create index, you must create index manually; dbconfig = "" contractParse = true topics = [ @@ -719,17 +783,17 @@ event.subscribe = { triggerName = "block" // block trigger, the value can't be modified enable = false topic = "block" // plugin topic, the value could be modified - solidified = false // if set true, just need solidified block, default is false + solidified = false // if set true, just need solidified block. Default: false }, { triggerName = "transaction" enable = false topic = "transaction" solidified = false - ethCompatible = false // if set true, add transactionIndex, cumulativeEnergyUsed, preCumulativeLogCount, logList, energyUnitPrice, default is false + ethCompatible = false // if set true, add transactionIndex, cumulativeEnergyUsed, preCumulativeLogCount, logList, energyUnitPrice. Default: false }, { - triggerName = "contractevent" + triggerName = "contractevent" // contractevent represents contractlog data decoded by the ABI. enable = false topic = "contractevent" }, @@ -741,7 +805,7 @@ event.subscribe = { }, { triggerName = "solidity" // solidity block trigger(just include solidity block number and timestamp), the value can't be modified - enable = true // the default value is true + enable = true // Default: true topic = "solidity" }, { @@ -769,4 +833,3 @@ event.subscribe = { ] } } - From 560200f36dd8b06fac980cc4d58e3f6159f1f5d4 Mon Sep 17 00:00:00 2001 From: 0xbigapple Date: Fri, 29 Aug 2025 15:25:21 +0800 Subject: [PATCH 42/59] fix(jsonrpc): support blockHash param and genesis block process for eth_getBlockReceipts (#6433) * fix: return null for genesis block in eth_getBlockReceipts * feat:support query eth_getBlockReceipts by blockHash * checkStyle --- .../core/services/jsonrpc/TronJsonRpc.java | 2 +- .../services/jsonrpc/TronJsonRpcImpl.java | 20 ++++++++--- .../tron/core/jsonrpc/JsonrpcServiceTest.java | 35 +++++++++---------- 3 files changed, 33 insertions(+), 24 deletions(-) diff --git a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpc.java b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpc.java index 5e6fc771c80..658c8051038 100644 --- a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpc.java +++ b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpc.java @@ -151,7 +151,7 @@ TransactionResult getTransactionByBlockNumberAndIndex(String blockNumOrTag, Stri @JsonRpcError(exception = JsonRpcInvalidParamsException.class, code = -32602, data = "{}"), @JsonRpcError(exception = JsonRpcInternalException.class, code = -32000, data = "{}") }) - List getBlockReceipts(String blockNumOrTag) + List getBlockReceipts(String blockNumOrHashOrTag) throws JsonRpcInvalidParamsException, JsonRpcInternalException; @JsonRpcMethod("eth_call") diff --git a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java index f681a94fe9f..7a1518038d6 100644 --- a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java +++ b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java @@ -839,17 +839,27 @@ private TransactionContext findTransactionContext(TransactionInfoList infoList, /** * Get all transaction receipts for a specific block - * @param blockNumOrTag the block number or tag (latest, earliest, pending, finalized) + * @param blockNumOrHashOrTag blockNumber or blockHash or tag, + * tag includes: latest, earliest, pending, finalized * @return List of TransactionReceipt objects for all transactions in the block, * null if block not found * @throws JsonRpcInvalidParamsException if the parameter format is invalid * @throws JsonRpcInternalException if there's an internal error */ @Override - public List getBlockReceipts(String blockNumOrTag) + public List getBlockReceipts(String blockNumOrHashOrTag) throws JsonRpcInvalidParamsException, JsonRpcInternalException { - Block block = wallet.getByJsonBlockId(blockNumOrTag); - if (block == null) { + + Block block = null; + + if (Pattern.matches(HASH_REGEX, blockNumOrHashOrTag)) { + block = getBlockByJsonHash(blockNumOrHashOrTag); + } else { + block = wallet.getByJsonBlockId(blockNumOrHashOrTag); + } + + // block receipts not available: block is genesis, not produced yet, or pruned in light node + if (block == null || block.getBlockHeader().getRawData().getNumber() == 0) { return null; } @@ -857,7 +867,7 @@ public List getBlockReceipts(String blockNumOrTag) long blockNum = blockCapsule.getNum(); TransactionInfoList transactionInfoList = wallet.getTransactionInfoByBlockNum(blockNum); - //energy price at the block timestamp + // energy price at the block timestamp long energyFee = wallet.getEnergyFee(blockCapsule.getTimeStamp()); // Validate transaction list size consistency diff --git a/framework/src/test/java/org/tron/core/jsonrpc/JsonrpcServiceTest.java b/framework/src/test/java/org/tron/core/jsonrpc/JsonrpcServiceTest.java index f6f4a35c1b7..81db38ce286 100644 --- a/framework/src/test/java/org/tron/core/jsonrpc/JsonrpcServiceTest.java +++ b/framework/src/test/java/org/tron/core/jsonrpc/JsonrpcServiceTest.java @@ -164,23 +164,6 @@ public void init() { dbManager.getTransactionStore() .put(transactionCapsule3.getTransactionId().getBytes(), transactionCapsule3); - blockCapsule0.getTransactions().forEach(tx -> { - TransactionCapsule transactionCapsule = new TransactionCapsule(tx.getInstance()); - transactionCapsule.setBlockNum(blockCapsule0.getNum()); - dbManager.getTransactionStore() - .put(transactionCapsule.getTransactionId().getBytes(), transactionCapsule); - }); - - TransactionRetCapsule transactionRetCapsule0 = new TransactionRetCapsule(); - blockCapsule0.getTransactions().forEach(tx -> { - TransactionInfoCapsule transactionInfoCapsule = new TransactionInfoCapsule(); - transactionInfoCapsule.setId(tx.getTransactionId().getBytes()); - transactionInfoCapsule.setBlockNumber(blockCapsule0.getNum()); - transactionRetCapsule0.addTransactionInfo(transactionInfoCapsule.getInstance()); - }); - dbManager.getTransactionRetStore().put( - ByteArray.fromLong(blockCapsule0.getNum()), transactionRetCapsule0); - List logs = new ArrayList<>(); logs.add(Protocol.TransactionInfo.Log.newBuilder() .setAddress(ByteString.copyFrom("address1".getBytes())) @@ -340,6 +323,8 @@ public void testGetBlockByNumber() { } Assert.assertEquals(ByteArray.toJsonHex(0L), blockResult.getNumber()); Assert.assertEquals(ByteArray.toJsonHex(blockCapsule0.getNum()), blockResult.getNumber()); + Assert.assertEquals(blockResult.getTransactions().length, + blockCapsule0.getTransactions().size()); // latest try { @@ -1048,7 +1033,7 @@ public void testGetBlockReceipts() { try { List transactionReceiptList = tronJsonRpc.getBlockReceipts("earliest"); - Assert.assertFalse(transactionReceiptList.isEmpty()); + Assert.assertNull(transactionReceiptList); } catch (JsonRpcInvalidParamsException | JsonRpcInternalException e) { throw new RuntimeException(e); } @@ -1088,6 +1073,20 @@ public void testGetBlockReceipts() { throw new RuntimeException(e); } + try { + String blockHash = blockCapsule1.getBlockId().toString(); + List transactionReceiptList + = tronJsonRpc.getBlockReceipts(blockHash); + List transactionReceiptList2 + = tronJsonRpc.getBlockReceipts("0x" + blockHash); + + Assert.assertFalse(transactionReceiptList.isEmpty()); + Assert.assertEquals(JSON.toJSONString(transactionReceiptList), + JSON.toJSONString(transactionReceiptList2)); + } catch (JsonRpcInvalidParamsException | JsonRpcInternalException e) { + throw new RuntimeException(e); + } + } @Test From 02f81c6e842091b854540ebe856664f352abb735 Mon Sep 17 00:00:00 2001 From: 317787106 <317787106@qq.com> Date: Fri, 5 Sep 2025 15:27:33 +0800 Subject: [PATCH 43/59] fix(test): release resource of testcases gracefully (#6437) * delete BaseNet, BaseNetTest * release resource of gRPC * optimize testcase to reduce the time cost * delete db after testcase in tmp dir; short the time cost of some case; optimize some shutdown action * use shutdownnow instead of shutdown in close Channel --- .../tron/core/db2/core/SnapshotManager.java | 3 + .../tron/common/logsfilter/FilterQuery.java | 14 +-- .../application/TronApplicationContext.java | 2 + .../common/logsfilter/EventPluginLoader.java | 9 +- .../java/org/tron/core/config/args/Args.java | 4 +- .../test/java/org/tron/common/EntityTest.java | 39 ++++++++ .../tron/common/backup/BackupServerTest.java | 3 +- .../logsfilter/NativeMessageQueueTest.java | 19 ++-- .../org/tron/common/utils/FileUtilTest.java | 1 + .../common/utils/client/Configuration.java | 11 +-- .../common/utils/client/utils/HttpMethed.java | 2 +- .../core/CreateCommonTransactionTest.java | 47 --------- .../actuator/vm/ProgramTraceListenerTest.java | 28 +++++- .../core/actuator/vm/SerializersTest.java | 12 +++ .../core/event/HistoryEventServiceTest.java | 1 + .../java/org/tron/core/jsonrpc/BloomTest.java | 2 +- .../core/jsonrpc/ConcurrentHashMapTest.java | 10 +- .../tron/core/jsonrpc/LogBlockQueryTest.java | 8 ++ .../core/jsonrpc/SectionBloomStoreTest.java | 2 + .../prometheus/PrometheusApiServiceTest.java | 2 +- .../test/java/org/tron/core/net/BaseNet.java | 97 ------------------- .../java/org/tron/core/net/BaseNetTest.java | 16 --- .../services/EffectiveCheckServiceTest.java | 43 +++----- .../core/services/DelegationServiceTest.java | 81 ++++++---------- .../core/services/NodeInfoServiceTest.java | 41 ++++---- .../core/services/RpcApiServicesTest.java | 24 ++++- .../org/tron/core/services/WalletApiTest.java | 33 +++++-- .../LiteFnQueryGrpcInterceptorTest.java | 7 +- .../filter/RpcApiAccessInterceptorTest.java | 18 +++- .../tron/core/zksnark/LibrustzcashTest.java | 1 + .../org/tron/program/SolidityNodeTest.java | 9 +- framework/src/test/resources/logback-test.xml | 4 +- .../java/org/tron/plugins/DbCopyTest.java | 8 +- .../java/org/tron/plugins/DbLiteTest.java | 2 +- .../test/java/org/tron/plugins/DbTest.java | 10 +- 35 files changed, 271 insertions(+), 342 deletions(-) delete mode 100644 framework/src/test/java/org/tron/core/CreateCommonTransactionTest.java create mode 100644 framework/src/test/java/org/tron/core/actuator/vm/SerializersTest.java delete mode 100644 framework/src/test/java/org/tron/core/net/BaseNet.java delete mode 100644 framework/src/test/java/org/tron/core/net/BaseNetTest.java diff --git a/chainbase/src/main/java/org/tron/core/db2/core/SnapshotManager.java b/chainbase/src/main/java/org/tron/core/db2/core/SnapshotManager.java index eb27141a82c..bfcb6237003 100644 --- a/chainbase/src/main/java/org/tron/core/db2/core/SnapshotManager.java +++ b/chainbase/src/main/java/org/tron/core/db2/core/SnapshotManager.java @@ -427,6 +427,9 @@ public List getCheckpointList() { } private void deleteCheckpoint() { + if(checkTmpStore == null) { + return; + } try { Map hmap = new HashMap<>(); for (Map.Entry e : checkTmpStore.getDbSource()) { diff --git a/common/src/main/java/org/tron/common/logsfilter/FilterQuery.java b/common/src/main/java/org/tron/common/logsfilter/FilterQuery.java index b2d0fc428e4..2ae50370129 100644 --- a/common/src/main/java/org/tron/common/logsfilter/FilterQuery.java +++ b/common/src/main/java/org/tron/common/logsfilter/FilterQuery.java @@ -31,12 +31,7 @@ public static long parseFromBlockNumber(String blockNum) { if (StringUtils.isEmpty(blockNum) || FilterQuery.EARLIEST.equalsIgnoreCase(blockNum)) { number = FilterQuery.EARLIEST_BLOCK_NUM; } else { - try { - number = Long.parseLong(blockNum); - } catch (Exception e) { - logger.error("invalid filter: fromBlockNumber: {}", blockNum); - throw e; - } + number = Long.parseLong(blockNum); } return number; } @@ -46,12 +41,7 @@ public static long parseToBlockNumber(String blockNum) { if (StringUtils.isEmpty(blockNum) || FilterQuery.LATEST.equalsIgnoreCase(blockNum)) { number = FilterQuery.LATEST_BLOCK_NUM; } else { - try { - number = Long.parseLong(blockNum); - } catch (Exception e) { - logger.error("invalid filter: toBlockNumber: {}", blockNum); - throw e; - } + number = Long.parseLong(blockNum); } return number; } diff --git a/framework/src/main/java/org/tron/common/application/TronApplicationContext.java b/framework/src/main/java/org/tron/common/application/TronApplicationContext.java index 64edec77c9c..6c934528f4f 100644 --- a/framework/src/main/java/org/tron/common/application/TronApplicationContext.java +++ b/framework/src/main/java/org/tron/common/application/TronApplicationContext.java @@ -13,8 +13,10 @@ public TronApplicationContext(DefaultListableBeanFactory beanFactory) { super(beanFactory); } + //only used for testcase public TronApplicationContext(Class... annotatedClasses) { super(annotatedClasses); + this.registerShutdownHook(); } public TronApplicationContext(String... basePackages) { diff --git a/framework/src/main/java/org/tron/common/logsfilter/EventPluginLoader.java b/framework/src/main/java/org/tron/common/logsfilter/EventPluginLoader.java index 0a7a5ac3a76..6d16be50164 100644 --- a/framework/src/main/java/org/tron/common/logsfilter/EventPluginLoader.java +++ b/framework/src/main/java/org/tron/common/logsfilter/EventPluginLoader.java @@ -9,7 +9,6 @@ import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; - import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.bouncycastle.util.encoders.Hex; @@ -26,7 +25,6 @@ import org.tron.common.logsfilter.trigger.SolidityTrigger; import org.tron.common.logsfilter.trigger.TransactionLogTrigger; import org.tron.common.logsfilter.trigger.Trigger; -import org.tron.common.utils.JsonUtil; @Slf4j public class EventPluginLoader { @@ -140,7 +138,7 @@ public static boolean matchFilter(ContractTrigger trigger) { private static boolean filterContractAddress(ContractTrigger trigger, List addressList) { addressList = addressList.stream().filter(item -> - org.apache.commons.lang3.StringUtils.isNotEmpty(item)) + org.apache.commons.lang3.StringUtils.isNotEmpty(item)) .collect(Collectors.toList()); if (Objects.isNull(addressList) || addressList.isEmpty()) { return true; @@ -173,7 +171,7 @@ private static boolean filterContractTopicList(ContractTrigger trigger, List(((ContractEventTrigger) trigger).getTopicMap().values()); } else if (trigger != null) { hset = trigger.getLogInfo().getClonedTopics() - .stream().map(Hex::toHexString).collect(Collectors.toSet()); + .stream().map(Hex::toHexString).collect(Collectors.toSet()); } for (String top : topList) { @@ -547,6 +545,9 @@ public boolean isBusy() { return false; } int queueSize = 0; + if (eventListeners == null || eventListeners.isEmpty()) { + return false; + } for (IPluginEventListener listener : eventListeners) { try { queueSize += listener.getPendingSize(); diff --git a/framework/src/main/java/org/tron/core/config/args/Args.java b/framework/src/main/java/org/tron/core/config/args/Args.java index 4583db56d97..b2972e950b2 100644 --- a/framework/src/main/java/org/tron/core/config/args/Args.java +++ b/framework/src/main/java/org/tron/core/config/args/Args.java @@ -1621,7 +1621,7 @@ private static FilterQuery getEventFilter(final com.typesafe.config.Config confi try { fromBlockLong = FilterQuery.parseFromBlockNumber(fromBlock); } catch (Exception e) { - logger.error("{}", e); + logger.error("invalid filter: fromBlockNumber: {}", fromBlock, e); return null; } filter.setFromBlock(fromBlockLong); @@ -1630,7 +1630,7 @@ private static FilterQuery getEventFilter(final com.typesafe.config.Config confi try { toBlockLong = FilterQuery.parseToBlockNumber(toBlock); } catch (Exception e) { - logger.error("{}", e); + logger.error("invalid filter: toBlockNumber: {}", toBlock, e); return null; } filter.setToBlock(toBlockLong); diff --git a/framework/src/test/java/org/tron/common/EntityTest.java b/framework/src/test/java/org/tron/common/EntityTest.java index 483475a453b..bbdc8631225 100644 --- a/framework/src/test/java/org/tron/common/EntityTest.java +++ b/framework/src/test/java/org/tron/common/EntityTest.java @@ -5,13 +5,16 @@ import static org.junit.Assert.assertTrue; import com.google.common.collect.Lists; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import org.apache.commons.collections4.CollectionUtils; import org.junit.Before; import org.junit.Test; import org.tron.common.entity.NodeInfo; import org.tron.common.entity.NodeInfo.MachineInfo; import org.tron.common.entity.NodeInfo.MachineInfo.DeadLockThreadInfo; +import org.tron.common.entity.PeerInfo; public class EntityTest { @@ -54,6 +57,9 @@ public void testDeadLockThreadInfo() { @Test public void testNodeInfo() { + List peerInfoList = new ArrayList<>(); + peerInfoList.add(getDefaultPeerInfo()); + NodeInfo nodeInfo = new NodeInfo(); nodeInfo.setTotalFlow(1L); nodeInfo.setCheatWitnessInfoMap(new HashMap<>()); @@ -62,6 +68,39 @@ public void testNodeInfo() { nodeInfo.setMachineInfo(machineInfo); nodeInfo.setBlock("block"); nodeInfo.setSolidityBlock("solidityBlock"); + nodeInfo.setPeerList(peerInfoList); nodeInfo.transferToProtoEntity(); } + + private PeerInfo getDefaultPeerInfo() { + PeerInfo peerInfo = new PeerInfo(); + peerInfo.setAvgLatency(peerInfo.getAvgLatency()); + peerInfo.setBlockInPorcSize(peerInfo.getBlockInPorcSize()); + peerInfo.setConnectTime(peerInfo.getConnectTime()); + peerInfo.setDisconnectTimes(peerInfo.getDisconnectTimes()); + peerInfo.setHeadBlockTimeWeBothHave(peerInfo.getHeadBlockTimeWeBothHave()); + peerInfo.setHeadBlockWeBothHave(peerInfo.getHeadBlockWeBothHave()); + peerInfo.setHost("host"); + peerInfo.setInFlow(peerInfo.getInFlow()); + peerInfo.setLastBlockUpdateTime(peerInfo.getLastBlockUpdateTime()); + peerInfo.setLastSyncBlock("last"); + peerInfo.setLocalDisconnectReason("localDisconnectReason"); + peerInfo.setNodeCount(peerInfo.getNodeCount()); + peerInfo.setNodeId("nodeId"); + peerInfo.setHeadBlockWeBothHave("headBlockWeBothHave"); + peerInfo.setRemainNum(peerInfo.getRemainNum()); + peerInfo.setRemoteDisconnectReason("remoteDisconnectReason"); + peerInfo.setScore(peerInfo.getScore()); + peerInfo.setPort(peerInfo.getPort()); + peerInfo.setSyncFlag(peerInfo.isSyncFlag()); + peerInfo.setNeedSyncFromPeer(peerInfo.isNeedSyncFromPeer()); + peerInfo.setNeedSyncFromUs(peerInfo.isNeedSyncFromUs()); + peerInfo.setSyncToFetchSize(peerInfo.getSyncToFetchSize()); + peerInfo.setSyncToFetchSizePeekNum(peerInfo.getSyncToFetchSizePeekNum()); + peerInfo.setSyncBlockRequestedSize(peerInfo.getSyncBlockRequestedSize()); + peerInfo.setUnFetchSynNum(peerInfo.getUnFetchSynNum()); + peerInfo.setActive(peerInfo.isActive()); + + return peerInfo; + } } diff --git a/framework/src/test/java/org/tron/common/backup/BackupServerTest.java b/framework/src/test/java/org/tron/common/backup/BackupServerTest.java index c40aca7e17a..18e264eead2 100644 --- a/framework/src/test/java/org/tron/common/backup/BackupServerTest.java +++ b/framework/src/test/java/org/tron/common/backup/BackupServerTest.java @@ -10,6 +10,7 @@ import org.junit.rules.Timeout; import org.tron.common.backup.socket.BackupServer; import org.tron.common.parameter.CommonParameter; +import org.tron.common.utils.PublicMethod; import org.tron.core.Constant; import org.tron.core.config.args.Args; @@ -26,7 +27,7 @@ public class BackupServerTest { @Before public void setUp() throws Exception { Args.setParam(new String[]{"-d", temporaryFolder.newFolder().toString()}, Constant.TEST_CONF); - CommonParameter.getInstance().setBackupPort(80); + CommonParameter.getInstance().setBackupPort(PublicMethod.chooseRandomPort()); List members = new ArrayList<>(); members.add("127.0.0.2"); CommonParameter.getInstance().setBackupMembers(members); diff --git a/framework/src/test/java/org/tron/common/logsfilter/NativeMessageQueueTest.java b/framework/src/test/java/org/tron/common/logsfilter/NativeMessageQueueTest.java index e6bb407bb53..d356e43d66c 100644 --- a/framework/src/test/java/org/tron/common/logsfilter/NativeMessageQueueTest.java +++ b/framework/src/test/java/org/tron/common/logsfilter/NativeMessageQueueTest.java @@ -55,18 +55,19 @@ public void publishTrigger() { public void startSubscribeThread() { Thread thread = new Thread(() -> { - ZContext context = new ZContext(); - ZMQ.Socket subscriber = context.createSocket(SocketType.SUB); + try (ZContext context = new ZContext()) { + ZMQ.Socket subscriber = context.createSocket(SocketType.SUB); - Assert.assertEquals(true, subscriber.connect(String.format("tcp://localhost:%d", bindPort))); - Assert.assertEquals(true, subscriber.subscribe(topic)); + Assert.assertTrue(subscriber.connect(String.format("tcp://localhost:%d", bindPort))); + Assert.assertTrue(subscriber.subscribe(topic)); - while (!Thread.currentThread().isInterrupted()) { - byte[] message = subscriber.recv(); - String triggerMsg = new String(message); - - Assert.assertEquals(true, triggerMsg.contains(dataToSend) || triggerMsg.contains(topic)); + while (!Thread.currentThread().isInterrupted()) { + byte[] message = subscriber.recv(); + String triggerMsg = new String(message); + Assert.assertTrue(triggerMsg.contains(dataToSend) || triggerMsg.contains(topic)); + } + // ZMQ.Socket will be automatically closed when ZContext is closed } }); thread.start(); diff --git a/framework/src/test/java/org/tron/common/utils/FileUtilTest.java b/framework/src/test/java/org/tron/common/utils/FileUtilTest.java index 126e0918520..c22e83760a1 100644 --- a/framework/src/test/java/org/tron/common/utils/FileUtilTest.java +++ b/framework/src/test/java/org/tron/common/utils/FileUtilTest.java @@ -67,6 +67,7 @@ public void testReadData_NormalFile() throws IOException { try (FileWriter writer = new FileWriter(tempFile.toFile())) { writer.write("Hello, World!"); } + tempFile.toFile().deleteOnExit(); char[] buffer = new char[1024]; int len = readData(tempFile.toString(), buffer); diff --git a/framework/src/test/java/org/tron/common/utils/client/Configuration.java b/framework/src/test/java/org/tron/common/utils/client/Configuration.java index 79dded303aa..fb253b9605b 100644 --- a/framework/src/test/java/org/tron/common/utils/client/Configuration.java +++ b/framework/src/test/java/org/tron/common/utils/client/Configuration.java @@ -4,7 +4,6 @@ import com.typesafe.config.ConfigFactory; import java.io.File; import java.io.FileInputStream; -import java.io.FileNotFoundException; import java.io.InputStreamReader; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; @@ -28,12 +27,12 @@ public static Config getByPath(final String configurationPath) { if (config == null) { File configFile = new File(System.getProperty("user.dir") + '/' + configurationPath); if (configFile.exists()) { - try { - config = ConfigFactory - .parseReader(new InputStreamReader(new FileInputStream(configurationPath))); + try (FileInputStream fis = new FileInputStream(configurationPath); + InputStreamReader isr = new InputStreamReader(fis)) { + config = ConfigFactory.parseReader(isr); logger.info("use user defined config file in current dir"); - } catch (FileNotFoundException e) { - logger.error("load user defined config file exception: " + e.getMessage()); + } catch (Exception e) { + logger.error("load user defined config file exception: {}", e.getMessage()); } } else { config = ConfigFactory.load(configurationPath); diff --git a/framework/src/test/java/org/tron/common/utils/client/utils/HttpMethed.java b/framework/src/test/java/org/tron/common/utils/client/utils/HttpMethed.java index a68bb616f11..030fbd80dea 100644 --- a/framework/src/test/java/org/tron/common/utils/client/utils/HttpMethed.java +++ b/framework/src/test/java/org/tron/common/utils/client/utils/HttpMethed.java @@ -2231,7 +2231,7 @@ public static void waitToProduceOneBlock(String httpNode) { } Integer nextBlockNum = 0; Integer times = 0; - while (nextBlockNum <= currentBlockNum + 1 && times++ <= 10) { + while (nextBlockNum < currentBlockNum + 1 && times++ <= 6) { response = HttpMethed.getNowBlock(httpNode); responseContent = HttpMethed.parseResponseContent(response); if (responseContent.containsKey("block_header")) { diff --git a/framework/src/test/java/org/tron/core/CreateCommonTransactionTest.java b/framework/src/test/java/org/tron/core/CreateCommonTransactionTest.java deleted file mode 100644 index 4bcef1e148c..00000000000 --- a/framework/src/test/java/org/tron/core/CreateCommonTransactionTest.java +++ /dev/null @@ -1,47 +0,0 @@ -package org.tron.core; - -import static org.tron.common.utils.client.WalletClient.decodeFromBase58Check; - -import com.google.protobuf.Any; -import com.google.protobuf.ByteString; -import io.grpc.ManagedChannelBuilder; -import org.tron.api.GrpcAPI.TransactionExtention; -import org.tron.api.WalletGrpc; -import org.tron.api.WalletGrpc.WalletBlockingStub; -import org.tron.protos.Protocol.Transaction; -import org.tron.protos.Protocol.Transaction.Contract; -import org.tron.protos.Protocol.Transaction.Contract.ContractType; -import org.tron.protos.Protocol.Transaction.raw; -import org.tron.protos.contract.StorageContract.UpdateBrokerageContract; - -public class CreateCommonTransactionTest { - - private static final String FULL_NODE = "127.0.0.1:50051"; - - /** - * for example create UpdateBrokerageContract - */ - public static void testCreateUpdateBrokerageContract() { - WalletBlockingStub walletStub = WalletGrpc - .newBlockingStub(ManagedChannelBuilder.forTarget(FULL_NODE).usePlaintext().build()); - UpdateBrokerageContract.Builder updateBrokerageContract = UpdateBrokerageContract.newBuilder(); - updateBrokerageContract.setOwnerAddress( - ByteString.copyFrom(decodeFromBase58Check("TN3zfjYUmMFK3ZsHSsrdJoNRtGkQmZLBLz"))) - .setBrokerage(10); - Transaction.Builder transaction = Transaction.newBuilder(); - raw.Builder raw = Transaction.raw.newBuilder(); - Contract.Builder contract = Contract.newBuilder(); - contract.setType(ContractType.UpdateBrokerageContract) - .setParameter(Any.pack(updateBrokerageContract.build())); - raw.addContract(contract.build()); - transaction.setRawData(raw.build()); - TransactionExtention transactionExtention = walletStub - .createCommonTransaction(transaction.build()); - System.out.println("Common UpdateBrokerage: " + transactionExtention); - } - - public static void main(String[] args) { - testCreateUpdateBrokerageContract(); - } - -} diff --git a/framework/src/test/java/org/tron/core/actuator/vm/ProgramTraceListenerTest.java b/framework/src/test/java/org/tron/core/actuator/vm/ProgramTraceListenerTest.java index 089711219f8..770e2bd0ea5 100644 --- a/framework/src/test/java/org/tron/core/actuator/vm/ProgramTraceListenerTest.java +++ b/framework/src/test/java/org/tron/core/actuator/vm/ProgramTraceListenerTest.java @@ -2,6 +2,7 @@ import java.io.IOException; import java.lang.reflect.Field; +import java.util.ArrayList; import java.util.List; import java.util.Map; import lombok.extern.slf4j.Slf4j; @@ -15,12 +16,15 @@ import org.tron.core.Constant; import org.tron.core.config.args.Args; import org.tron.core.db.TransactionStoreTest; +import org.tron.core.vm.trace.Op; import org.tron.core.vm.trace.OpActions; import org.tron.core.vm.trace.OpActions.Action; +import org.tron.core.vm.trace.ProgramTrace; import org.tron.core.vm.trace.ProgramTraceListener; @Slf4j(topic = "VM") public class ProgramTraceListenerTest { + @ClassRule public static TemporaryFolder temporaryFolder = new TemporaryFolder(); @@ -33,7 +37,7 @@ public class ProgramTraceListenerTest { @BeforeClass public static void init() throws IOException { - Args.setParam(new String[]{"--output-directory", + Args.setParam(new String[] {"--output-directory", temporaryFolder.newFolder().toString(), "--debug"}, Constant.TEST_CONF); } @@ -130,7 +134,6 @@ private void validateProgramTraceListener() { Assert.assertFalse(e instanceof IllegalAccessException); } - traceListener.resetActions(); try { @@ -179,4 +182,25 @@ public void programTraceListenerTest() { validateDisableTraceListener(); } + @Test + public void testGetSet() { + ProgramTrace programTrace = new ProgramTrace(); + Op op = new Op(); + List ops = new ArrayList<>(); + ops.add(op); + programTrace.setOps(ops); + programTrace.setResult("result"); + programTrace.setContractAddress("contractAddress"); + programTrace.setError("error"); + programTrace.result(new byte[] {}); + programTrace.error(new Exception()); + programTrace.getOps(); + programTrace.getContractAddress(); + programTrace.getError(); + programTrace.getResult(); + programTrace.toString(); + + Assert.assertTrue(true); + } + } diff --git a/framework/src/test/java/org/tron/core/actuator/vm/SerializersTest.java b/framework/src/test/java/org/tron/core/actuator/vm/SerializersTest.java new file mode 100644 index 00000000000..52ee1eeb937 --- /dev/null +++ b/framework/src/test/java/org/tron/core/actuator/vm/SerializersTest.java @@ -0,0 +1,12 @@ +package org.tron.core.actuator.vm; + +import org.junit.Test; +import org.tron.core.vm.trace.Serializers; + +public class SerializersTest { + + @Test + public void testSerializeFieldsOnly() { + Serializers.serializeFieldsOnly("testString", true); + } +} diff --git a/framework/src/test/java/org/tron/core/event/HistoryEventServiceTest.java b/framework/src/test/java/org/tron/core/event/HistoryEventServiceTest.java index 49f77ccf597..d56f1fc0993 100644 --- a/framework/src/test/java/org/tron/core/event/HistoryEventServiceTest.java +++ b/framework/src/test/java/org/tron/core/event/HistoryEventServiceTest.java @@ -43,6 +43,7 @@ public void test() throws Exception { RealtimeEventService realtimeEventService = new RealtimeEventService(); BlockEventLoad blockEventLoad = new BlockEventLoad(); ReflectUtils.setFieldValue(blockEventLoad, "instance", instance); + ReflectUtils.setFieldValue(blockEventLoad, "manager", manager); ReflectUtils.setFieldValue(historyEventService, "solidEventService", solidEventService); ReflectUtils.setFieldValue(historyEventService, "realtimeEventService", realtimeEventService); diff --git a/framework/src/test/java/org/tron/core/jsonrpc/BloomTest.java b/framework/src/test/java/org/tron/core/jsonrpc/BloomTest.java index 19e232b0f93..c88615d2d2f 100644 --- a/framework/src/test/java/org/tron/core/jsonrpc/BloomTest.java +++ b/framework/src/test/java/org/tron/core/jsonrpc/BloomTest.java @@ -131,7 +131,7 @@ private TransactionInfo createTransactionInfo(byte[] address1, byte[] address2) @Test public void benchmarkCreateByTransaction() { - int times = 10000; + int times = 1000; // small TransactionRetCapsule smallCapsule = new TransactionRetCapsule(); diff --git a/framework/src/test/java/org/tron/core/jsonrpc/ConcurrentHashMapTest.java b/framework/src/test/java/org/tron/core/jsonrpc/ConcurrentHashMapTest.java index 1de106f68e8..abb73671161 100644 --- a/framework/src/test/java/org/tron/core/jsonrpc/ConcurrentHashMapTest.java +++ b/framework/src/test/java/org/tron/core/jsonrpc/ConcurrentHashMapTest.java @@ -31,7 +31,7 @@ private static int randomInt(int minInt, int maxInt) { */ @Test public void testHandleBlockHash() { - int times = 200; + int times = 100; int eachCount = 200; Map conMap = TronJsonRpcImpl.getBlockFilter2ResultFull(); @@ -67,7 +67,7 @@ public void run() { TronJsonRpcImpl.handleBLockFilter(blockFilterCapsule); } try { - Thread.sleep(randomInt(100, 200)); + Thread.sleep(randomInt(50, 100)); } catch (InterruptedException e) { e.printStackTrace(); } @@ -81,7 +81,7 @@ public void run() { for (int t = 1; t <= times * 2; t++) { try { - Thread.sleep(randomInt(100, 200)); + Thread.sleep(randomInt(50, 100)); } catch (InterruptedException e) { e.printStackTrace(); } @@ -111,7 +111,7 @@ public void run() { for (int t = 1; t <= times * 2; t++) { try { - Thread.sleep(randomInt(100, 200)); + Thread.sleep(randomInt(50, 100)); } catch (InterruptedException e) { e.printStackTrace(); } @@ -144,7 +144,7 @@ public void run() { for (int t = 1; t <= times * 2; t++) { try { - Thread.sleep(randomInt(100, 200)); + Thread.sleep(randomInt(50, 100)); } catch (InterruptedException e) { e.printStackTrace(); } diff --git a/framework/src/test/java/org/tron/core/jsonrpc/LogBlockQueryTest.java b/framework/src/test/java/org/tron/core/jsonrpc/LogBlockQueryTest.java index deae8babb32..7442e92c8f5 100644 --- a/framework/src/test/java/org/tron/core/jsonrpc/LogBlockQueryTest.java +++ b/framework/src/test/java/org/tron/core/jsonrpc/LogBlockQueryTest.java @@ -5,6 +5,7 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import javax.annotation.Resource; +import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; @@ -49,6 +50,13 @@ public void setup() throws Exception { sectionBloomStore.put(1, 3, bitSet2); } + @After + public void tearDown() { + if (sectionExecutor != null && !sectionExecutor.isShutdown()) { + sectionExecutor.shutdown(); + } + } + @Test public void testPartialMatch() throws Exception { // Create a basic LogFilterWrapper diff --git a/framework/src/test/java/org/tron/core/jsonrpc/SectionBloomStoreTest.java b/framework/src/test/java/org/tron/core/jsonrpc/SectionBloomStoreTest.java index 111370bfffd..953465b299e 100644 --- a/framework/src/test/java/org/tron/core/jsonrpc/SectionBloomStoreTest.java +++ b/framework/src/test/java/org/tron/core/jsonrpc/SectionBloomStoreTest.java @@ -235,5 +235,7 @@ public void testWriteAndQuery() { } catch (Exception e) { Assert.fail(); } + + sectionExecutor.shutdownNow(); } } diff --git a/framework/src/test/java/org/tron/core/metrics/prometheus/PrometheusApiServiceTest.java b/framework/src/test/java/org/tron/core/metrics/prometheus/PrometheusApiServiceTest.java index 195e4749209..37c376ce9af 100644 --- a/framework/src/test/java/org/tron/core/metrics/prometheus/PrometheusApiServiceTest.java +++ b/framework/src/test/java/org/tron/core/metrics/prometheus/PrometheusApiServiceTest.java @@ -41,7 +41,7 @@ public class PrometheusApiServiceTest extends BaseTest { static LocalDateTime localDateTime = LocalDateTime.now(); @Resource private DposSlot dposSlot; - final int blocks = 512; + final int blocks = 100; private final String key = PublicMethod.getRandomPrivateKey(); private final byte[] privateKey = ByteArray.fromHexString(key); private static final AtomicInteger port = new AtomicInteger(0); diff --git a/framework/src/test/java/org/tron/core/net/BaseNet.java b/framework/src/test/java/org/tron/core/net/BaseNet.java deleted file mode 100644 index bdaab1b4301..00000000000 --- a/framework/src/test/java/org/tron/core/net/BaseNet.java +++ /dev/null @@ -1,97 +0,0 @@ -package org.tron.core.net; - -import java.io.IOException; -import java.util.Collection; -import java.util.Objects; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import lombok.extern.slf4j.Slf4j; -import org.junit.AfterClass; -import org.junit.Assert; -import org.junit.BeforeClass; -import org.junit.ClassRule; -import org.junit.rules.TemporaryFolder; -import org.tron.common.application.Application; -import org.tron.common.application.ApplicationFactory; -import org.tron.common.application.TronApplicationContext; -import org.tron.common.parameter.CommonParameter; -import org.tron.common.utils.PublicMethod; -import org.tron.common.utils.ReflectUtils; -import org.tron.core.Constant; -import org.tron.core.config.DefaultConfig; -import org.tron.core.config.args.Args; -import org.tron.core.net.peer.PeerConnection; - -@Slf4j -public class BaseNet { - - @ClassRule - public static final TemporaryFolder temporaryFolder = new TemporaryFolder(); - private static String dbDirectory = "net-database"; - private static String indexDirectory = "net-index"; - private static int port = 10000; - - protected static TronApplicationContext context; - - private static Application appT; - private static TronNetDelegate tronNetDelegate; - - private static ExecutorService executorService = Executors.newFixedThreadPool(1); - - @BeforeClass - public static void init() throws Exception { - executorService.execute(() -> { - logger.info("Full node running."); - try { - Args.setParam( - new String[]{ - "--output-directory", temporaryFolder.newFolder().toString(), - "--storage-db-directory", dbDirectory, - "--storage-index-directory", indexDirectory - }, - "config.conf" - ); - } catch (IOException e) { - Assert.fail("create temp db directory failed"); - } - CommonParameter parameter = Args.getInstance(); - parameter.setNodeListenPort(port); - parameter.getSeedNode().getAddressList().clear(); - parameter.setNodeExternalIp(Constant.LOCAL_HOST); - parameter.setRpcEnable(true); - parameter.setRpcPort(PublicMethod.chooseRandomPort()); - parameter.setRpcSolidityEnable(false); - parameter.setRpcPBFTEnable(false); - parameter.setFullNodeHttpEnable(false); - parameter.setSolidityNodeHttpEnable(false); - parameter.setPBFTHttpEnable(false); - context = new TronApplicationContext(DefaultConfig.class); - appT = ApplicationFactory.create(context); - appT.startup(); - try { - Thread.sleep(2000); - } catch (InterruptedException e) { - //ignore - } - tronNetDelegate = context.getBean(TronNetDelegate.class); - appT.blockUntilShutdown(); - }); - int tryTimes = 0; - do { - Thread.sleep(3000); //coverage consumerInvToSpread,consumerInvToFetch in AdvService.init - } while (++tryTimes < 100 && tronNetDelegate == null); - } - - @AfterClass - public static void destroy() { - if (Objects.nonNull(tronNetDelegate)) { - Collection peerConnections = ReflectUtils - .invokeMethod(tronNetDelegate, "getActivePeer"); - for (PeerConnection peer : peerConnections) { - peer.getChannel().close(); - } - } - Args.clearParam(); - context.destroy(); - } -} diff --git a/framework/src/test/java/org/tron/core/net/BaseNetTest.java b/framework/src/test/java/org/tron/core/net/BaseNetTest.java deleted file mode 100644 index fd0847c1f08..00000000000 --- a/framework/src/test/java/org/tron/core/net/BaseNetTest.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.tron.core.net; - -import lombok.extern.slf4j.Slf4j; -import org.junit.Test; -import org.tron.core.services.DelegationServiceTest; -import org.tron.core.services.NodeInfoServiceTest; - -@Slf4j -public class BaseNetTest extends BaseNet { - - @Test - public void test() { - new NodeInfoServiceTest(context).test(); - new DelegationServiceTest(context).test(); - } -} diff --git a/framework/src/test/java/org/tron/core/net/services/EffectiveCheckServiceTest.java b/framework/src/test/java/org/tron/core/net/services/EffectiveCheckServiceTest.java index 9104b087d26..da7f714d096 100644 --- a/framework/src/test/java/org/tron/core/net/services/EffectiveCheckServiceTest.java +++ b/framework/src/test/java/org/tron/core/net/services/EffectiveCheckServiceTest.java @@ -1,48 +1,35 @@ package org.tron.core.net.services; -import java.io.IOException; import java.lang.reflect.Method; import java.net.InetSocketAddress; -import org.junit.After; +import javax.annotation.Resource; import org.junit.Assert; -import org.junit.Before; -import org.junit.Rule; +import org.junit.BeforeClass; import org.junit.Test; -import org.junit.rules.TemporaryFolder; -import org.tron.common.application.TronApplicationContext; +import org.tron.common.BaseTest; +import org.tron.common.utils.PublicMethod; import org.tron.common.utils.ReflectUtils; import org.tron.core.Constant; -import org.tron.core.config.DefaultConfig; import org.tron.core.config.args.Args; import org.tron.core.net.TronNetService; import org.tron.core.net.service.effective.EffectiveCheckService; import org.tron.p2p.P2pConfig; -public class EffectiveCheckServiceTest { +public class EffectiveCheckServiceTest extends BaseTest { - protected TronApplicationContext context; + @Resource private EffectiveCheckService service; + @Resource + private TronNetService tronNetService; - @Rule - public final TemporaryFolder temporaryFolder = new TemporaryFolder(); - - @Before - public void init() throws IOException { - Args.setParam(new String[] {"--output-directory", - temporaryFolder.newFolder().toString(), "--debug"}, Constant.TEST_CONF); - context = new TronApplicationContext(DefaultConfig.class); - service = context.getBean(EffectiveCheckService.class); - } - - @After - public void destroy() { - Args.clearParam(); - context.destroy(); + @BeforeClass + public static void init() { + Args.setParam(new String[] {"--output-directory", dbPath(), "--debug"}, + Constant.TEST_CONF); } @Test public void testNoIpv4() throws Exception { - TronNetService tronNetService = context.getBean(TronNetService.class); Method privateMethod = tronNetService.getClass() .getDeclaredMethod("updateConfig", P2pConfig.class); privateMethod.setAccessible(true); @@ -54,10 +41,10 @@ public void testNoIpv4() throws Exception { @Test public void testFind() { - TronNetService tronNetService = context.getBean(TronNetService.class); + int port = PublicMethod.chooseRandomPort(); P2pConfig p2pConfig = new P2pConfig(); p2pConfig.setIp("127.0.0.1"); - p2pConfig.setPort(34567); + p2pConfig.setPort(port); ReflectUtils.setFieldValue(tronNetService, "p2pConfig", p2pConfig); TronNetService.getP2pService().start(p2pConfig); @@ -65,7 +52,7 @@ public void testFind() { Assert.assertNull(service.getCur()); ReflectUtils.invokeMethod(service, "resetCount"); - InetSocketAddress cur = new InetSocketAddress("192.168.0.1", 34567); + InetSocketAddress cur = new InetSocketAddress("192.168.0.1", port); service.setCur(cur); service.onDisconnect(cur); } diff --git a/framework/src/test/java/org/tron/core/services/DelegationServiceTest.java b/framework/src/test/java/org/tron/core/services/DelegationServiceTest.java index 5c898eb42d6..0ce4ba4d67d 100644 --- a/framework/src/test/java/org/tron/core/services/DelegationServiceTest.java +++ b/framework/src/test/java/org/tron/core/services/DelegationServiceTest.java @@ -4,49 +4,28 @@ import static org.tron.common.utils.client.Parameter.CommonConstant.ADD_PRE_FIX_BYTE_MAINNET; import com.google.protobuf.ByteString; -import io.grpc.ManagedChannelBuilder; +import javax.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.junit.Assert; -import org.tron.api.GrpcAPI.BytesMessage; -import org.tron.api.GrpcAPI.TransactionExtention; -import org.tron.api.WalletGrpc; -import org.tron.api.WalletGrpc.WalletBlockingStub; -import org.tron.common.application.TronApplicationContext; +import org.junit.BeforeClass; +import org.junit.Test; +import org.tron.common.BaseTest; +import org.tron.core.Constant; import org.tron.core.Wallet; import org.tron.core.capsule.AccountCapsule; -import org.tron.core.db.Manager; +import org.tron.core.config.args.Args; import org.tron.core.service.MortgageService; -import org.tron.protos.contract.StorageContract.UpdateBrokerageContract; @Slf4j -public class DelegationServiceTest { +public class DelegationServiceTest extends BaseTest { - private static String fullnode = "127.0.0.1:50051"; - private MortgageService mortgageService; - private Manager manager; + @Resource + protected MortgageService mortgageService; - public DelegationServiceTest(TronApplicationContext context) { - mortgageService = context.getBean(MortgageService.class); - manager = context.getBean(Manager.class); - } - - public static void testGrpc() { - WalletBlockingStub walletStub = WalletGrpc - .newBlockingStub(ManagedChannelBuilder.forTarget(fullnode) - .usePlaintext() - .build()); - BytesMessage.Builder builder = BytesMessage.newBuilder(); - builder.setValue(ByteString.copyFromUtf8("TLTDZBcPoJ8tZ6TTEeEqEvwYFk2wgotSfD")); - System.out - .println("getBrokerageInfo: " + walletStub.getBrokerageInfo(builder.build()).getNum()); - System.out.println("getRewardInfo: " + walletStub.getRewardInfo(builder.build()).getNum()); - UpdateBrokerageContract.Builder updateBrokerageContract = UpdateBrokerageContract.newBuilder(); - updateBrokerageContract.setOwnerAddress( - ByteString.copyFrom(decodeFromBase58Check("TN3zfjYUmMFK3ZsHSsrdJoNRtGkQmZLBLz"))) - .setBrokerage(10); - TransactionExtention transactionExtention = walletStub - .updateBrokerage(updateBrokerageContract.build()); - System.out.println("UpdateBrokerage: " + transactionExtention); + @BeforeClass + public static void init() { + Args.setParam(new String[] {"--output-directory", dbPath(), "--debug"}, + Constant.TESTNET_CONF); } private void testPay(int cycle) { @@ -59,7 +38,7 @@ private void testPay(int cycle) { mortgageService.payStandbyWitness(); Wallet.setAddressPreFixByte(ADD_PRE_FIX_BYTE_MAINNET); byte[] sr1 = decodeFromBase58Check("TLTDZBcPoJ8tZ6TTEeEqEvwYFk2wgotSfD"); - long value = manager.getDelegationStore().getReward(cycle, sr1); + long value = dbManager.getDelegationStore().getReward(cycle, sr1); long tmp = 0; for (int i = 0; i < 27; i++) { tmp += 100000000 + i; @@ -73,47 +52,47 @@ private void testPay(int cycle) { expect += 32000000; brokerageAmount = (long) (rate * 32000000); expect -= brokerageAmount; - value = manager.getDelegationStore().getReward(cycle, sr1); + value = dbManager.getDelegationStore().getReward(cycle, sr1); Assert.assertEquals(expect, value); } private void testWithdraw() { //init - manager.getDynamicPropertiesStore().saveCurrentCycleNumber(1); + dbManager.getDynamicPropertiesStore().saveCurrentCycleNumber(1); testPay(1); - manager.getDynamicPropertiesStore().saveCurrentCycleNumber(2); + dbManager.getDynamicPropertiesStore().saveCurrentCycleNumber(2); testPay(2); byte[] sr1 = decodeFromBase58Check("THKJYuUmMKKARNf7s2VT51g5uPY6KEqnat"); - AccountCapsule accountCapsule = manager.getAccountStore().get(sr1); + AccountCapsule accountCapsule = dbManager.getAccountStore().get(sr1); byte[] sr27 = decodeFromBase58Check("TLTDZBcPoJ8tZ6TTEeEqEvwYFk2wgotSfD"); accountCapsule.addVotes(ByteString.copyFrom(sr27), 10000000); - manager.getAccountStore().put(sr1, accountCapsule); + dbManager.getAccountStore().put(sr1, accountCapsule); // long allowance = accountCapsule.getAllowance(); long value = mortgageService.queryReward(sr1) - allowance; - long reward1 = (long) ((double) manager.getDelegationStore().getReward(0, sr27) / 100000000 + long reward1 = (long) ((double) dbManager.getDelegationStore().getReward(0, sr27) / 100000000 * 10000000); - long reward2 = (long) ((double) manager.getDelegationStore().getReward(1, sr27) / 100000000 + long reward2 = (long) ((double) dbManager.getDelegationStore().getReward(1, sr27) / 100000000 * 10000000); long reward = reward1 + reward2; - System.out.println("testWithdraw:" + value + ", reward:" + reward); Assert.assertEquals(reward, value); mortgageService.withdrawReward(sr1); - accountCapsule = manager.getAccountStore().get(sr1); + accountCapsule = dbManager.getAccountStore().get(sr1); allowance = accountCapsule.getAllowance() - allowance; System.out.println("withdrawReward:" + allowance); Assert.assertEquals(reward, allowance); } + @Test public void test() { - manager.getDynamicPropertiesStore().saveChangeDelegation(1); - manager.getDynamicPropertiesStore().saveConsensusLogicOptimization(1); + dbManager.getDynamicPropertiesStore().saveChangeDelegation(1); + dbManager.getDynamicPropertiesStore().saveConsensusLogicOptimization(1); byte[] sr27 = decodeFromBase58Check("TLTDZBcPoJ8tZ6TTEeEqEvwYFk2wgotSfD"); - manager.getDelegationStore().setBrokerage(0, sr27, 10); - manager.getDelegationStore().setBrokerage(1, sr27, 20); - manager.getDelegationStore().setWitnessVote(0, sr27, 100000000); - manager.getDelegationStore().setWitnessVote(1, sr27, 100000000); - manager.getDelegationStore().setWitnessVote(2, sr27, 100000000); + dbManager.getDelegationStore().setBrokerage(0, sr27, 10); + dbManager.getDelegationStore().setBrokerage(1, sr27, 20); + dbManager.getDelegationStore().setWitnessVote(0, sr27, 100000000); + dbManager.getDelegationStore().setWitnessVote(1, sr27, 100000000); + dbManager.getDelegationStore().setWitnessVote(2, sr27, 100000000); testPay(0); testWithdraw(); } diff --git a/framework/src/test/java/org/tron/core/services/NodeInfoServiceTest.java b/framework/src/test/java/org/tron/core/services/NodeInfoServiceTest.java index be0a96f632b..24b17a0bf1b 100644 --- a/framework/src/test/java/org/tron/core/services/NodeInfoServiceTest.java +++ b/framework/src/test/java/org/tron/core/services/NodeInfoServiceTest.java @@ -1,35 +1,37 @@ package org.tron.core.services; -import static org.mockito.Mockito.mock; - import com.alibaba.fastjson.JSON; import com.google.protobuf.ByteString; -import java.net.InetSocketAddress; +import javax.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.junit.Assert; -import org.mockito.Mockito; -import org.tron.common.application.TronApplicationContext; +import org.junit.BeforeClass; +import org.junit.Test; +import org.tron.common.BaseTest; import org.tron.common.entity.NodeInfo; import org.tron.common.utils.Sha256Hash; +import org.tron.core.Constant; import org.tron.core.capsule.BlockCapsule; -import org.tron.core.net.P2pEventHandlerImpl; -import org.tron.p2p.connection.Channel; +import org.tron.core.config.args.Args; import org.tron.program.Version; @Slf4j -public class NodeInfoServiceTest { +public class NodeInfoServiceTest extends BaseTest { + + @Resource + protected NodeInfoService nodeInfoService; + @Resource + protected WitnessProductBlockService witnessProductBlockService; - private NodeInfoService nodeInfoService; - private WitnessProductBlockService witnessProductBlockService; - private P2pEventHandlerImpl p2pEventHandler; - public NodeInfoServiceTest(TronApplicationContext context) { - nodeInfoService = context.getBean("nodeInfoService", NodeInfoService.class); - witnessProductBlockService = context.getBean(WitnessProductBlockService.class); - p2pEventHandler = context.getBean(P2pEventHandlerImpl.class); + @BeforeClass + public static void init() { + Args.setParam(new String[] {"--output-directory", dbPath(), "--debug"}, + Constant.TEST_CONF); } + @Test public void test() { BlockCapsule blockCapsule1 = new BlockCapsule(1, Sha256Hash.ZERO_HASH, 100, ByteString.EMPTY); @@ -38,17 +40,10 @@ public void test() { witnessProductBlockService.validWitnessProductTwoBlock(blockCapsule1); witnessProductBlockService.validWitnessProductTwoBlock(blockCapsule2); - //add peer - InetSocketAddress a1 = new InetSocketAddress("127.0.0.1", 10001); - Channel c1 = mock(Channel.class); - Mockito.when(c1.getInetSocketAddress()).thenReturn(a1); - Mockito.when(c1.getInetAddress()).thenReturn(a1.getAddress()); - p2pEventHandler.onConnect(c1); - //test setConnectInfo NodeInfo nodeInfo = nodeInfoService.getNodeInfo(); Assert.assertEquals(nodeInfo.getConfigNodeInfo().getCodeVersion(), Version.getVersion()); - Assert.assertEquals(nodeInfo.getCheatWitnessInfoMap().size(), 1); + Assert.assertEquals(1, nodeInfo.getCheatWitnessInfoMap().size()); logger.info("{}", JSON.toJSONString(nodeInfo)); } diff --git a/framework/src/test/java/org/tron/core/services/RpcApiServicesTest.java b/framework/src/test/java/org/tron/core/services/RpcApiServicesTest.java index d5bad3f802c..419a875df82 100644 --- a/framework/src/test/java/org/tron/core/services/RpcApiServicesTest.java +++ b/framework/src/test/java/org/tron/core/services/RpcApiServicesTest.java @@ -110,7 +110,12 @@ @FixMethodOrder(MethodSorters.NAME_ASCENDING) public class RpcApiServicesTest { + + private static Application appTest; private static TronApplicationContext context; + private static ManagedChannel channelFull = null; + private static ManagedChannel channelPBFT = null; + private static ManagedChannel channelSolidity = null; private static DatabaseBlockingStub databaseBlockingStubFull = null; private static DatabaseBlockingStub databaseBlockingStubSolidity = null; private static DatabaseBlockingStub databaseBlockingStubPBFT = null; @@ -131,7 +136,7 @@ public class RpcApiServicesTest { @BeforeClass public static void init() throws IOException { - Args.setParam(new String[]{"-d", temporaryFolder.newFolder().toString()}, Constant.TEST_CONF); + Args.setParam(new String[] {"-d", temporaryFolder.newFolder().toString()}, Constant.TEST_CONF); Assert.assertEquals(5, getInstance().getRpcMaxRstStream()); Assert.assertEquals(10, getInstance().getRpcSecondsPerWindow()); String OWNER_ADDRESS = Wallet.getAddressPreFixString() @@ -152,13 +157,13 @@ public static void init() throws IOException { String pBFTNode = String.format("%s:%d", Constant.LOCAL_HOST, getInstance().getRpcOnPBFTPort()); - ManagedChannel channelFull = ManagedChannelBuilder.forTarget(fullNode) + channelFull = ManagedChannelBuilder.forTarget(fullNode) .usePlaintext() .build(); - ManagedChannel channelPBFT = ManagedChannelBuilder.forTarget(pBFTNode) + channelPBFT = ManagedChannelBuilder.forTarget(pBFTNode) .usePlaintext() .build(); - ManagedChannel channelSolidity = ManagedChannelBuilder.forTarget(solidityNode) + channelSolidity = ManagedChannelBuilder.forTarget(solidityNode) .usePlaintext() .build(); context = new TronApplicationContext(DefaultConfig.class); @@ -177,12 +182,21 @@ public static void init() throws IOException { manager.getAccountStore().put(ownerCapsule.createDbKey(), ownerCapsule); manager.getDynamicPropertiesStore().saveAllowShieldedTransaction(1); manager.getDynamicPropertiesStore().saveAllowShieldedTRC20Transaction(1); - Application appTest = ApplicationFactory.create(context); + appTest = ApplicationFactory.create(context); appTest.startup(); } @AfterClass public static void destroy() { + if (channelFull != null) { + channelFull.shutdownNow(); + } + if (channelPBFT != null) { + channelPBFT.shutdownNow(); + } + if (channelSolidity != null) { + channelSolidity.shutdownNow(); + } context.close(); Args.clearParam(); } diff --git a/framework/src/test/java/org/tron/core/services/WalletApiTest.java b/framework/src/test/java/org/tron/core/services/WalletApiTest.java index 8890d4bfd9e..ebae038ab5e 100644 --- a/framework/src/test/java/org/tron/core/services/WalletApiTest.java +++ b/framework/src/test/java/org/tron/core/services/WalletApiTest.java @@ -3,7 +3,7 @@ import io.grpc.ManagedChannelBuilder; import java.io.IOException; import lombok.extern.slf4j.Slf4j; -import org.junit.After; +import org.junit.AfterClass; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.ClassRule; @@ -45,18 +45,31 @@ public static void init() throws IOException { public void listNodesTest() { String fullNode = String.format("%s:%d", "127.0.0.1", Args.getInstance().getRpcPort()); - WalletGrpc.WalletBlockingStub walletStub = WalletGrpc - .newBlockingStub(ManagedChannelBuilder.forTarget(fullNode) - .usePlaintext() - .build()); - Assert.assertTrue(walletStub.listNodes(EmptyMessage.getDefaultInstance()) - .getNodesList().isEmpty()); + io.grpc.ManagedChannel channel = ManagedChannelBuilder.forTarget(fullNode) + .usePlaintext() + .build(); + try { + WalletGrpc.WalletBlockingStub walletStub = WalletGrpc.newBlockingStub(channel); + Assert.assertTrue(walletStub.listNodes(EmptyMessage.getDefaultInstance()) + .getNodesList().isEmpty()); + } finally { + // Properly shutdown the gRPC channel to prevent resource leaks + channel.shutdown(); + try { + if (!channel.awaitTermination(5, java.util.concurrent.TimeUnit.SECONDS)) { + channel.shutdownNow(); + } + } catch (InterruptedException e) { + channel.shutdownNow(); + Thread.currentThread().interrupt(); + } + } } - @After - public void destroy() { - Args.clearParam(); + @AfterClass + public static void destroy() { context.destroy(); + Args.clearParam(); } } diff --git a/framework/src/test/java/org/tron/core/services/filter/LiteFnQueryGrpcInterceptorTest.java b/framework/src/test/java/org/tron/core/services/filter/LiteFnQueryGrpcInterceptorTest.java index d98e2c9267e..d98eb9b5384 100644 --- a/framework/src/test/java/org/tron/core/services/filter/LiteFnQueryGrpcInterceptorTest.java +++ b/framework/src/test/java/org/tron/core/services/filter/LiteFnQueryGrpcInterceptorTest.java @@ -75,7 +75,6 @@ public static void init() throws IOException { .usePlaintext() .build(); context = new TronApplicationContext(DefaultConfig.class); - context.registerShutdownHook(); blockingStubFull = WalletGrpc.newBlockingStub(channelFull); blockingStubSolidity = WalletSolidityGrpc.newBlockingStub(channelSolidity); blockingStubpBFT = WalletSolidityGrpc.newBlockingStub(channelpBFT); @@ -90,13 +89,13 @@ public static void init() throws IOException { @AfterClass public static void destroy() throws InterruptedException { if (channelFull != null) { - channelFull.shutdown().awaitTermination(5, TimeUnit.SECONDS); + channelFull.shutdownNow(); } if (channelSolidity != null) { - channelSolidity.shutdown().awaitTermination(5, TimeUnit.SECONDS); + channelSolidity.shutdownNow(); } if (channelpBFT != null) { - channelpBFT.shutdown().awaitTermination(5, TimeUnit.SECONDS); + channelpBFT.shutdownNow(); } context.close(); Args.clearParam(); diff --git a/framework/src/test/java/org/tron/core/services/filter/RpcApiAccessInterceptorTest.java b/framework/src/test/java/org/tron/core/services/filter/RpcApiAccessInterceptorTest.java index ce7efabef0c..9b48f0ffb98 100644 --- a/framework/src/test/java/org/tron/core/services/filter/RpcApiAccessInterceptorTest.java +++ b/framework/src/test/java/org/tron/core/services/filter/RpcApiAccessInterceptorTest.java @@ -42,6 +42,9 @@ public class RpcApiAccessInterceptorTest { private static TronApplicationContext context; + private static ManagedChannel channelFull = null; + private static ManagedChannel channelPBFT = null; + private static ManagedChannel channelSolidity = null; private static WalletGrpc.WalletBlockingStub blockingStubFull = null; private static WalletSolidityGrpc.WalletSolidityBlockingStub blockingStubSolidity = null; private static WalletSolidityGrpc.WalletSolidityBlockingStub blockingStubPBFT = null; @@ -68,13 +71,13 @@ public static void init() throws IOException { String pBFTNode = String.format("%s:%d", Constant.LOCAL_HOST, Args.getInstance().getRpcOnPBFTPort()); - ManagedChannel channelFull = ManagedChannelBuilder.forTarget(fullNode) + channelFull = ManagedChannelBuilder.forTarget(fullNode) .usePlaintext() .build(); - ManagedChannel channelPBFT = ManagedChannelBuilder.forTarget(pBFTNode) + channelPBFT = ManagedChannelBuilder.forTarget(pBFTNode) .usePlaintext() .build(); - ManagedChannel channelSolidity = ManagedChannelBuilder.forTarget(solidityNode) + channelSolidity = ManagedChannelBuilder.forTarget(solidityNode) .usePlaintext() .build(); @@ -93,6 +96,15 @@ public static void init() throws IOException { */ @AfterClass public static void destroy() { + if (channelFull != null) { + channelFull.shutdownNow(); + } + if (channelPBFT != null) { + channelPBFT.shutdownNow(); + } + if (channelSolidity != null) { + channelSolidity.shutdownNow(); + } context.close(); Args.clearParam(); } diff --git a/framework/src/test/java/org/tron/core/zksnark/LibrustzcashTest.java b/framework/src/test/java/org/tron/core/zksnark/LibrustzcashTest.java index f3852952cc7..5d403b54f90 100644 --- a/framework/src/test/java/org/tron/core/zksnark/LibrustzcashTest.java +++ b/framework/src/test/java/org/tron/core/zksnark/LibrustzcashTest.java @@ -297,6 +297,7 @@ public void calBenchmarkSpendConcurrent() throws Exception { })); countDownLatch.await(); + generatePool.shutdown(); logger.info("generate cost time:" + (System.currentTimeMillis() - startGenerate)); } diff --git a/framework/src/test/java/org/tron/program/SolidityNodeTest.java b/framework/src/test/java/org/tron/program/SolidityNodeTest.java index cb2be8cd688..80fb2a74607 100755 --- a/framework/src/test/java/org/tron/program/SolidityNodeTest.java +++ b/framework/src/test/java/org/tron/program/SolidityNodeTest.java @@ -9,6 +9,7 @@ import org.junit.Test; import org.tron.common.BaseTest; import org.tron.common.client.DatabaseGrpcClient; +import org.tron.common.utils.PublicMethod; import org.tron.core.Constant; import org.tron.core.config.args.Args; import org.tron.core.exception.TronError; @@ -24,9 +25,13 @@ public class SolidityNodeTest extends BaseTest { RpcApiService rpcApiService; @Resource SolidityNodeHttpApiService solidityNodeHttpApiService; + static int rpcPort = PublicMethod.chooseRandomPort(); + static int solidityHttpPort = PublicMethod.chooseRandomPort(); static { - Args.setParam(new String[]{"-d", dbPath(), "--solidity"}, Constant.TEST_CONF); + Args.setParam(new String[] {"-d", dbPath(), "--solidity"}, Constant.TEST_CONF); + Args.getInstance().setRpcPort(rpcPort); + Args.getInstance().setSolidityHttpPort(solidityHttpPort); } @Test @@ -45,7 +50,7 @@ public void testSolidityArgs() { public void testSolidityGrpcCall() { rpcApiService.start(); DatabaseGrpcClient databaseGrpcClient = null; - String address = Args.getInstance().getTrustNodeAddr(); + String address = Args.getInstance().getTrustNodeAddr().split(":")[0] + ":" + rpcPort; try { databaseGrpcClient = new DatabaseGrpcClient(address); } catch (Exception e) { diff --git a/framework/src/test/resources/logback-test.xml b/framework/src/test/resources/logback-test.xml index d05c7fca79e..9cf4a04062f 100644 --- a/framework/src/test/resources/logback-test.xml +++ b/framework/src/test/resources/logback-test.xml @@ -34,7 +34,7 @@ - - + + diff --git a/plugins/src/test/java/org/tron/plugins/DbCopyTest.java b/plugins/src/test/java/org/tron/plugins/DbCopyTest.java index 7548812b799..571fd8f5aa7 100644 --- a/plugins/src/test/java/org/tron/plugins/DbCopyTest.java +++ b/plugins/src/test/java/org/tron/plugins/DbCopyTest.java @@ -14,7 +14,7 @@ public class DbCopyTest extends DbTest { public void testRunForLevelDB() throws RocksDBException, IOException { init(DbTool.DbType.LevelDB); String[] args = new String[] { "db", "cp", INPUT_DIRECTORY, - genarateTmpDir()}; + generateTmpDir()}; Assert.assertEquals(0, cli.execute(args)); } @@ -22,7 +22,7 @@ public void testRunForLevelDB() throws RocksDBException, IOException { public void testRunForRocksDB() throws RocksDBException, IOException { init(DbTool.DbType.RocksDB); String[] args = new String[] { "db", "cp", INPUT_DIRECTORY, - genarateTmpDir()}; + generateTmpDir()}; Assert.assertEquals(0, cli.execute(args)); } @@ -43,7 +43,7 @@ public void testNotExist() { @Test public void testEmpty() throws IOException { String[] args = new String[] {"db", "cp", temporaryFolder.newFolder().toString(), - genarateTmpDir()}; + generateTmpDir()}; Assert.assertEquals(0, cli.execute(args)); } @@ -57,7 +57,7 @@ public void testDestIsExist() throws IOException { @Test public void testSrcIsFile() throws IOException { String[] args = new String[] {"db", "cp", temporaryFolder.newFile().toString(), - genarateTmpDir()}; + generateTmpDir()}; Assert.assertEquals(403, cli.execute(args)); } diff --git a/plugins/src/test/java/org/tron/plugins/DbLiteTest.java b/plugins/src/test/java/org/tron/plugins/DbLiteTest.java index b0c7f3b96be..47494812ef3 100644 --- a/plugins/src/test/java/org/tron/plugins/DbLiteTest.java +++ b/plugins/src/test/java/org/tron/plugins/DbLiteTest.java @@ -62,7 +62,7 @@ public void startApp() { */ public void shutdown() throws InterruptedException { if (channelFull != null) { - channelFull.shutdown().awaitTermination(5, TimeUnit.SECONDS); + channelFull.shutdownNow(); } context.close(); } diff --git a/plugins/src/test/java/org/tron/plugins/DbTest.java b/plugins/src/test/java/org/tron/plugins/DbTest.java index 9337723e8ff..da693a720c2 100644 --- a/plugins/src/test/java/org/tron/plugins/DbTest.java +++ b/plugins/src/test/java/org/tron/plugins/DbTest.java @@ -21,7 +21,6 @@ public class DbTest { private static final String ACCOUNT = "account"; private static final String MARKET = DBUtils.MARKET_PAIR_PRICE_TO_ORDER; public CommandLine cli = new CommandLine(new Toolkit()); - String tmpDir = System.getProperty("java.io.tmpdir"); @Rule public final TemporaryFolder temporaryFolder = new TemporaryFolder(); @@ -51,7 +50,7 @@ private static void initDB(String sourceDir, String dbName, DbTool.DbType dbType } return; } - try (DBInterface db = DbTool.getDB(sourceDir, dbName, dbType)) { + try (DBInterface db = DbTool.getDB(sourceDir, dbName, dbType)) { if (MARKET.equalsIgnoreCase(dbName)) { byte[] sellTokenID1 = ByteArray.fromString("100"); byte[] buyTokenID1 = ByteArray.fromString("200"); @@ -74,7 +73,6 @@ private static void initDB(String sourceDir, String dbName, DbTool.DbType dbType 2003L ); - //Use out-of-order insertion,key in store should be 1,2,3 db.put(pairPriceKey1, "1".getBytes(StandardCharsets.UTF_8)); db.put(pairPriceKey2, "2".getBytes(StandardCharsets.UTF_8)); @@ -90,10 +88,12 @@ private static void initDB(String sourceDir, String dbName, DbTool.DbType dbType /** * Generate a not-exist temporary directory path. + * * @return temporary path */ - public String genarateTmpDir() { - File dir = Paths.get(tmpDir, UUID.randomUUID().toString()).toFile(); + public String generateTmpDir() throws IOException { + File dir = Paths.get(temporaryFolder.newFolder().toString(), UUID.randomUUID().toString()) + .toFile(); dir.deleteOnExit(); return dir.getPath(); } From 3a1a5d0e5dffddf605d90712281ece0ccb7b3f04 Mon Sep 17 00:00:00 2001 From: halibobo1205 <82020050+halibobo1205@users.noreply.github.com> Date: Tue, 9 Sep 2025 17:23:01 +0800 Subject: [PATCH 44/59] feat(db): update to rocksdb 9 for arm (#6440) * feat(db): update to rocks9 for arm * feat(rocksdb): release option when db is created --- build.gradle | 2 +- .../tron/common/storage/OptionsPicker.java | 15 ---- .../leveldb/LevelDbDataSourceImpl.java | 50 ++++++------- .../rocksdb/RocksDbDataSourceImpl.java | 55 +++++++------- .../org/tron/common/utils/StorageUtils.java | 5 ++ .../java/org/tron/core/db/TronDatabase.java | 16 +---- .../tron/core/db/TronStoreWithRevoking.java | 14 +--- .../tron/core/db/common/DbSourceInter.java | 2 - .../org/tron/core/db2/common/TxCacheDB.java | 12 +--- .../tron/common/setting/RocksDbSettings.java | 14 ++++ framework/src/main/resources/config.conf | 2 + .../leveldb/LevelDbDataSourceImplTest.java | 59 ++------------- .../rocksdb/RocksDbDataSourceImplTest.java | 72 +++---------------- .../java/org/tron/core/db2/ChainbaseTest.java | 5 +- gradle/verification-metadata.xml | 10 +-- 15 files changed, 101 insertions(+), 232 deletions(-) delete mode 100644 chainbase/src/main/java/org/tron/common/storage/OptionsPicker.java diff --git a/build.gradle b/build.gradle index 000cc05479b..a77c5918752 100644 --- a/build.gradle +++ b/build.gradle @@ -29,7 +29,7 @@ ext.archInfo = [ ], requires: [ JavaVersion: isArm64 ? JavaVersion.VERSION_17 : JavaVersion.VERSION_1_8, - RocksdbVersion: isArm64 ? '7.7.3' : '5.15.10' + RocksdbVersion: isArm64 ? '9.7.4' : '5.15.10' ], VMOptions: isArm64 ? "${rootDir}/gradle/jdk17/java-tron.vmoptions" : "${rootDir}/gradle/java-tron.vmoptions" ] diff --git a/chainbase/src/main/java/org/tron/common/storage/OptionsPicker.java b/chainbase/src/main/java/org/tron/common/storage/OptionsPicker.java deleted file mode 100644 index acace12b39d..00000000000 --- a/chainbase/src/main/java/org/tron/common/storage/OptionsPicker.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.tron.common.storage; - -import org.tron.common.setting.RocksDbSettings; -import org.tron.common.utils.StorageUtils; - -public class OptionsPicker { - - protected org.iq80.leveldb.Options getOptionsByDbNameForLevelDB(String dbName) { - return StorageUtils.getOptionsByDbName(dbName); - } - - protected org.rocksdb.Options getOptionsByDbNameForRocksDB(String dbName) { - return RocksDbSettings.getOptionsByDbName(dbName); - } -} diff --git a/chainbase/src/main/java/org/tron/common/storage/leveldb/LevelDbDataSourceImpl.java b/chainbase/src/main/java/org/tron/common/storage/leveldb/LevelDbDataSourceImpl.java index 54cdbf96ca1..c48800573e1 100644 --- a/chainbase/src/main/java/org/tron/common/storage/leveldb/LevelDbDataSourceImpl.java +++ b/chainbase/src/main/java/org/tron/common/storage/leveldb/LevelDbDataSourceImpl.java @@ -41,12 +41,10 @@ import lombok.extern.slf4j.Slf4j; import org.iq80.leveldb.DB; import org.iq80.leveldb.DBIterator; -import org.iq80.leveldb.Logger; import org.iq80.leveldb.Options; import org.iq80.leveldb.ReadOptions; import org.iq80.leveldb.WriteBatch; import org.iq80.leveldb.WriteOptions; -import org.slf4j.LoggerFactory; import org.tron.common.parameter.CommonParameter; import org.tron.common.storage.WriteOptionsWrapper; import org.tron.common.storage.metric.DbStat; @@ -70,28 +68,11 @@ public class LevelDbDataSourceImpl extends DbStat implements DbSourceInterCRITICAL: The returned iterator holds native resources and MUST be closed + * after use to prevent memory leaks. It is strongly recommended to use a try-with-resources + * statement. + * + *

Example of correct usage: + *

{@code
+   * try (DBIterator iterator = db.iterator()) {
+   *   while (iterator.hasNext()) {
+   *     // ... process entry
+   *   }
+   * }
+   * }
+ * + * @return a new database iterator that must be closed. + */ @Override public org.tron.core.db.common.iterator.DBIterator iterator() { return new StoreIterator(getDBIterator()); @@ -455,7 +455,7 @@ public Stream> stream() { @Override public LevelDbDataSourceImpl newInstance() { return new LevelDbDataSourceImpl(StorageUtils.getOutputDirectoryByDbName(dataBaseName), - dataBaseName, options, writeOptions); + dataBaseName); } private DBIterator getDBIterator() { diff --git a/chainbase/src/main/java/org/tron/common/storage/rocksdb/RocksDbDataSourceImpl.java b/chainbase/src/main/java/org/tron/common/storage/rocksdb/RocksDbDataSourceImpl.java index 4d5dd3b6fce..63191b6ac01 100644 --- a/chainbase/src/main/java/org/tron/common/storage/rocksdb/RocksDbDataSourceImpl.java +++ b/chainbase/src/main/java/org/tron/common/storage/rocksdb/RocksDbDataSourceImpl.java @@ -22,8 +22,6 @@ import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.rocksdb.Checkpoint; -import org.rocksdb.InfoLogLevel; -import org.rocksdb.Logger; import org.rocksdb.Options; import org.rocksdb.ReadOptions; import org.rocksdb.RocksDB; @@ -32,7 +30,6 @@ import org.rocksdb.Status; import org.rocksdb.WriteBatch; import org.rocksdb.WriteOptions; -import org.slf4j.LoggerFactory; import org.tron.common.error.TronDBException; import org.tron.common.setting.RocksDbSettings; import org.tron.common.storage.WriteOptionsWrapper; @@ -52,26 +49,16 @@ public class RocksDbDataSourceImpl extends DbStat implements DbSourceInterCRITICAL: The returned iterator holds native resources and MUST be closed + * after use to prevent memory leaks. It is strongly recommended to use a try-with-resources + * statement. + * + *

Example of correct usage: + *

{@code
+   * try (DBIterator iterator = db.iterator()) {
+   *   while (iterator.hasNext()) {
+   *     // ... process entry
+   *   }
+   * }
+   * }
+ * + * @return a new database iterator that must be closed. + */ @Override public org.tron.core.db.common.iterator.DBIterator iterator() { return new RockStoreIterator(getRocksIterator()); @@ -415,14 +414,15 @@ public Set getValuesNext(byte[] key, long limit) { public void backup(String dir) throws RocksDBException { throwIfNotAlive(); - Checkpoint cp = Checkpoint.create(database); - cp.createCheckpoint(dir + this.getDBName()); + try (Checkpoint cp = Checkpoint.create(database)) { + cp.createCheckpoint(dir + this.getDBName()); + } } private RocksIterator getRocksIterator() { - try ( ReadOptions readOptions = new ReadOptions().setFillCache(false)) { + try (ReadOptions readOptions = new ReadOptions().setFillCache(false)) { throwIfNotAlive(); - return database.newIterator(readOptions); + return database.newIterator(readOptions); } } @@ -432,8 +432,7 @@ public boolean deleteDbBakPath(String dir) { @Override public RocksDbDataSourceImpl newInstance() { - return new RocksDbDataSourceImpl(parentPath, dataBaseName, - this.options); + return new RocksDbDataSourceImpl(parentPath, dataBaseName); } diff --git a/chainbase/src/main/java/org/tron/common/utils/StorageUtils.java b/chainbase/src/main/java/org/tron/common/utils/StorageUtils.java index 375f0ba92fb..0c7c77bd23f 100644 --- a/chainbase/src/main/java/org/tron/common/utils/StorageUtils.java +++ b/chainbase/src/main/java/org/tron/common/utils/StorageUtils.java @@ -1,16 +1,20 @@ package org.tron.common.utils; import static org.tron.common.parameter.CommonParameter.ENERGY_LIMIT_HARD_FORK; +import static org.tron.core.db.common.DbSourceInter.LEVELDB; import java.io.File; import org.apache.commons.lang3.StringUtils; import org.iq80.leveldb.Options; +import org.slf4j.LoggerFactory; import org.tron.common.parameter.CommonParameter; import org.tron.core.Constant; public class StorageUtils { + private static final org.slf4j.Logger levelDbLogger = LoggerFactory.getLogger(LEVELDB); + public static boolean getEnergyLimitHardFork() { return ENERGY_LIMIT_HARD_FORK; } @@ -62,6 +66,7 @@ public static Options getOptionsByDbName(String dbName) { if (Constant.MARKET_PAIR_PRICE_TO_ORDER.equals(dbName)) { options.comparator(new MarketOrderPriceComparatorForLevelDB()); } + options.logger(message -> levelDbLogger.info("{} {}", dbName, message)); return options; } } diff --git a/chainbase/src/main/java/org/tron/core/db/TronDatabase.java b/chainbase/src/main/java/org/tron/core/db/TronDatabase.java index 8630fbdcdff..e699675408f 100644 --- a/chainbase/src/main/java/org/tron/core/db/TronDatabase.java +++ b/chainbase/src/main/java/org/tron/core/db/TronDatabase.java @@ -8,10 +8,8 @@ import javax.annotation.PostConstruct; import lombok.Getter; import lombok.extern.slf4j.Slf4j; -import org.iq80.leveldb.WriteOptions; import org.springframework.beans.factory.annotation.Autowired; import org.tron.common.parameter.CommonParameter; -import org.tron.common.storage.OptionsPicker; import org.tron.common.storage.WriteOptionsWrapper; import org.tron.common.storage.leveldb.LevelDbDataSourceImpl; import org.tron.common.storage.metric.DbStatService; @@ -24,7 +22,7 @@ import org.tron.core.exception.ItemNotFoundException; @Slf4j(topic = "DB") -public abstract class TronDatabase extends OptionsPicker implements ITronChainBase { +public abstract class TronDatabase implements ITronChainBase { protected DbSourceInter dbSource; @Getter @@ -40,21 +38,13 @@ protected TronDatabase(String dbName) { if ("LEVELDB".equals(CommonParameter.getInstance().getStorage() .getDbEngine().toUpperCase())) { - dbSource = - new LevelDbDataSourceImpl(StorageUtils.getOutputDirectoryByDbName(dbName), - dbName, - getOptionsByDbNameForLevelDB(dbName), - new WriteOptions().sync(CommonParameter.getInstance() - .getStorage().isDbSync())); + dbSource = new LevelDbDataSourceImpl(StorageUtils.getOutputDirectoryByDbName(dbName), dbName); } else if ("ROCKSDB".equals(CommonParameter.getInstance() .getStorage().getDbEngine().toUpperCase())) { String parentName = Paths.get(StorageUtils.getOutputDirectoryByDbName(dbName), CommonParameter.getInstance().getStorage().getDbDirectory()).toString(); - dbSource = - new RocksDbDataSourceImpl(parentName, dbName, getOptionsByDbNameForRocksDB(dbName)); + dbSource = new RocksDbDataSourceImpl(parentName, dbName); } - - dbSource.initDB(); } @PostConstruct diff --git a/chainbase/src/main/java/org/tron/core/db/TronStoreWithRevoking.java b/chainbase/src/main/java/org/tron/core/db/TronStoreWithRevoking.java index 4952b70478d..73b1b103d76 100644 --- a/chainbase/src/main/java/org/tron/core/db/TronStoreWithRevoking.java +++ b/chainbase/src/main/java/org/tron/core/db/TronStoreWithRevoking.java @@ -15,10 +15,8 @@ import javax.annotation.PostConstruct; import lombok.Getter; import lombok.extern.slf4j.Slf4j; -import org.iq80.leveldb.WriteOptions; import org.springframework.beans.factory.annotation.Autowired; import org.tron.common.parameter.CommonParameter; -import org.tron.common.storage.OptionsPicker; import org.tron.common.storage.leveldb.LevelDbDataSourceImpl; import org.tron.common.storage.metric.DbStatService; import org.tron.common.storage.rocksdb.RocksDbDataSourceImpl; @@ -38,7 +36,7 @@ @Slf4j(topic = "DB") -public abstract class TronStoreWithRevoking extends OptionsPicker implements ITronChainBase { +public abstract class TronStoreWithRevoking implements ITronChainBase { @Getter // only for unit test protected IRevokingDB revokingDB; @@ -58,18 +56,12 @@ protected TronStoreWithRevoking(String dbName) { String dbEngine = CommonParameter.getInstance().getStorage().getDbEngine(); if ("LEVELDB".equals(dbEngine.toUpperCase())) { this.db = new LevelDB( - new LevelDbDataSourceImpl(StorageUtils.getOutputDirectoryByDbName(dbName), - dbName, - getOptionsByDbNameForLevelDB(dbName), - new WriteOptions().sync(CommonParameter.getInstance() - .getStorage().isDbSync()))); + new LevelDbDataSourceImpl(StorageUtils.getOutputDirectoryByDbName(dbName), dbName)); } else if ("ROCKSDB".equals(dbEngine.toUpperCase())) { String parentPath = Paths .get(StorageUtils.getOutputDirectoryByDbName(dbName), CommonParameter .getInstance().getStorage().getDbDirectory()).toString(); - this.db = new RocksDB( - new RocksDbDataSourceImpl(parentPath, - dbName, getOptionsByDbNameForRocksDB(dbName))); + this.db = new RocksDB(new RocksDbDataSourceImpl(parentPath, dbName)); } else { throw new RuntimeException(String.format("db engine %s is error", dbEngine)); } diff --git a/chainbase/src/main/java/org/tron/core/db/common/DbSourceInter.java b/chainbase/src/main/java/org/tron/core/db/common/DbSourceInter.java index 7a353bf1872..21c0a0dff2a 100755 --- a/chainbase/src/main/java/org/tron/core/db/common/DbSourceInter.java +++ b/chainbase/src/main/java/org/tron/core/db/common/DbSourceInter.java @@ -41,8 +41,6 @@ public interface DbSourceInter extends BatchSourceInter, void setDBName(String name); - void initDB(); - boolean isAlive(); void closeDB(); diff --git a/chainbase/src/main/java/org/tron/core/db2/common/TxCacheDB.java b/chainbase/src/main/java/org/tron/core/db2/common/TxCacheDB.java index db9516d6203..31131de0866 100644 --- a/chainbase/src/main/java/org/tron/core/db2/common/TxCacheDB.java +++ b/chainbase/src/main/java/org/tron/core/db2/common/TxCacheDB.java @@ -31,11 +31,9 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.ArrayUtils; import org.bouncycastle.util.encoders.Hex; -import org.iq80.leveldb.WriteOptions; import org.tron.common.parameter.CommonParameter; import org.tron.common.prometheus.MetricKeys; import org.tron.common.prometheus.Metrics; -import org.tron.common.storage.OptionsPicker; import org.tron.common.storage.leveldb.LevelDbDataSourceImpl; import org.tron.common.storage.rocksdb.RocksDbDataSourceImpl; import org.tron.common.utils.ByteArray; @@ -49,7 +47,7 @@ import org.tron.core.store.DynamicPropertiesStore; @Slf4j(topic = "DB") -public class TxCacheDB extends OptionsPicker implements DB, Flusher { +public class TxCacheDB implements DB, Flusher { // > 65_536(= 2^16) blocks, that is the number of the reference block private static final long MAX_BLOCK_SIZE = 65536; @@ -106,17 +104,13 @@ public TxCacheDB(String name, RecentTransactionStore recentTransactionStore, String dbEngine = CommonParameter.getInstance().getStorage().getDbEngine(); if ("LEVELDB".equals(dbEngine.toUpperCase())) { this.persistentStore = new LevelDB( - new LevelDbDataSourceImpl(StorageUtils.getOutputDirectoryByDbName(name), - name, getOptionsByDbNameForLevelDB(name), - new WriteOptions().sync(CommonParameter.getInstance() - .getStorage().isDbSync()))); + new LevelDbDataSourceImpl(StorageUtils.getOutputDirectoryByDbName(name), name)); } else if ("ROCKSDB".equals(dbEngine.toUpperCase())) { String parentPath = Paths .get(StorageUtils.getOutputDirectoryByDbName(name), CommonParameter .getInstance().getStorage().getDbDirectory()).toString(); - this.persistentStore = new RocksDB( - new RocksDbDataSourceImpl(parentPath, name, getOptionsByDbNameForRocksDB(name))); + this.persistentStore = new RocksDB(new RocksDbDataSourceImpl(parentPath, name)); } else { throw new RuntimeException(String.format("db type: %s is not supported", dbEngine)); } diff --git a/common/src/main/java/org/tron/common/setting/RocksDbSettings.java b/common/src/main/java/org/tron/common/setting/RocksDbSettings.java index 5c2e5eacddf..be1761133bf 100644 --- a/common/src/main/java/org/tron/common/setting/RocksDbSettings.java +++ b/common/src/main/java/org/tron/common/setting/RocksDbSettings.java @@ -1,15 +1,20 @@ package org.tron.common.setting; +import static org.tron.core.Constant.ROCKSDB; + import java.util.Arrays; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.rocksdb.BlockBasedTableConfig; import org.rocksdb.BloomFilter; import org.rocksdb.ComparatorOptions; +import org.rocksdb.InfoLogLevel; import org.rocksdb.LRUCache; +import org.rocksdb.Logger; import org.rocksdb.Options; import org.rocksdb.RocksDB; import org.rocksdb.Statistics; +import org.slf4j.LoggerFactory; import org.tron.common.utils.MarketOrderPriceComparatorForRocksDB; import org.tron.core.Constant; @@ -54,6 +59,8 @@ public class RocksDbSettings { "GITLAB_CI" }; + private static final org.slf4j.Logger rocksDbLogger = LoggerFactory.getLogger(ROCKSDB); + private RocksDbSettings() { } @@ -161,6 +168,12 @@ public static Options getOptionsByDbName(String dbName) { Options options = new Options(); + options.setLogger(new Logger(options) { + @Override + protected void log(InfoLogLevel infoLogLevel, String logMsg) { + rocksDbLogger.info("{} {}", dbName, logMsg); + } + }); // most of these options are suggested by https://github.com/facebook/rocksdb/wiki/Set-Up-Options // general options @@ -196,6 +209,7 @@ public static Options getOptionsByDbName(String dbName) { } if (isRunningInCI()) { + options.optimizeForSmallDb(); // Disable fallocate calls to avoid issues with disk space options.setAllowFAllocate(false); // Set WAL size limits to avoid excessive disk diff --git a/framework/src/main/resources/config.conf b/framework/src/main/resources/config.conf index 6fc5fc91cea..081964d19af 100644 --- a/framework/src/main/resources/config.conf +++ b/framework/src/main/resources/config.conf @@ -32,6 +32,8 @@ storage { # You can customize the configuration for each database. Otherwise, the database settings will use # their defaults, and data will be stored in the "output-directory" or in the directory specified # by the "-d" or "--output-directory" option. Attention: name is a required field that must be set! + # In this configuration, the name and path properties take effect for both LevelDB and RocksDB storage engines, + # while the additional properties (such as createIfMissing, paranoidChecks, compressionType, etc.) only take effect when using LevelDB. properties = [ # { # name = "account", diff --git a/framework/src/test/java/org/tron/common/storage/leveldb/LevelDbDataSourceImplTest.java b/framework/src/test/java/org/tron/common/storage/leveldb/LevelDbDataSourceImplTest.java index b5cfd15761e..8fc05746fc8 100644 --- a/framework/src/test/java/org/tron/common/storage/leveldb/LevelDbDataSourceImplTest.java +++ b/framework/src/test/java/org/tron/common/storage/leveldb/LevelDbDataSourceImplTest.java @@ -112,7 +112,6 @@ public void testPutGet() { dataSourceTest.resetDb(); String key1 = PublicMethod.getRandomPrivateKey(); byte[] key = key1.getBytes(); - dataSourceTest.initDB(); String value1 = "50000"; byte[] value = value1.getBytes(); @@ -155,8 +154,6 @@ public void testReset() { public void testupdateByBatchInner() { LevelDbDataSourceImpl dataSource = new LevelDbDataSourceImpl( Args.getInstance().getOutputDirectory(), "test_updateByBatch"); - dataSource.initDB(); - dataSource.resetDb(); String key1 = PublicMethod.getRandomPrivateKey(); String value1 = "50000"; String key2 = PublicMethod.getRandomPrivateKey(); @@ -195,7 +192,6 @@ public void testupdateByBatchInner() { public void testdeleteData() { LevelDbDataSourceImpl dataSource = new LevelDbDataSourceImpl( Args.getInstance().getOutputDirectory(), "test_delete"); - dataSource.initDB(); String key1 = PublicMethod.getRandomPrivateKey(); byte[] key = key1.getBytes(); dataSource.deleteData(key); @@ -209,8 +205,6 @@ public void testdeleteData() { public void testallKeys() { LevelDbDataSourceImpl dataSource = new LevelDbDataSourceImpl( Args.getInstance().getOutputDirectory(), "test_find_key"); - dataSource.initDB(); - dataSource.resetDb(); String key1 = PublicMethod.getRandomPrivateKey(); byte[] key = key1.getBytes(); @@ -233,7 +227,6 @@ public void testallKeys() { @Test(timeout = 1000) public void testLockReleased() { - dataSourceTest.initDB(); // normal close dataSourceTest.closeDB(); // closing already closed db. @@ -248,8 +241,6 @@ public void testLockReleased() { public void allKeysTest() { LevelDbDataSourceImpl dataSource = new LevelDbDataSourceImpl( Args.getInstance().getOutputDirectory(), "test_allKeysTest_key"); - dataSource.initDB(); - dataSource.resetDb(); byte[] key = "0000000987b10fbb7f17110757321".getBytes(); byte[] value = "50000".getBytes(); @@ -262,7 +253,6 @@ public void allKeysTest() { logger.info(ByteArray.toStr(keyOne)); }); assertEquals(2, dataSource.allKeys().size()); - dataSource.resetDb(); dataSource.closeDB(); } @@ -288,26 +278,10 @@ private void putSomeKeyValue(LevelDbDataSourceImpl dataSource) { dataSource.putData(key4, value4); } - @Test - public void seekTest() { - LevelDbDataSourceImpl dataSource = new LevelDbDataSourceImpl( - Args.getInstance().getOutputDirectory(), "test_seek_key"); - dataSource.initDB(); - dataSource.resetDb(); - - putSomeKeyValue(dataSource); - Assert.assertTrue(true); - dataSource.resetDb(); - dataSource.closeDB(); - } - @Test public void getValuesNext() { LevelDbDataSourceImpl dataSource = new LevelDbDataSourceImpl( Args.getInstance().getOutputDirectory(), "test_getValuesNext_key"); - dataSource.initDB(); - dataSource.resetDb(); - putSomeKeyValue(dataSource); Set seekKeyLimitNext = dataSource.getValuesNext("0000000300".getBytes(), 2); HashSet hashSet = Sets.newHashSet(ByteArray.toStr(value3), ByteArray.toStr(value4)); @@ -322,7 +296,6 @@ public void getValuesNext() { public void testGetTotal() { LevelDbDataSourceImpl dataSource = new LevelDbDataSourceImpl( Args.getInstance().getOutputDirectory(), "test_getTotal_key"); - dataSource.initDB(); dataSource.resetDb(); Map dataMapset = Maps.newHashMap(); @@ -339,8 +312,6 @@ public void testGetTotal() { public void getKeysNext() { LevelDbDataSourceImpl dataSource = new LevelDbDataSourceImpl( Args.getInstance().getOutputDirectory(), "test_getKeysNext_key"); - dataSource.initDB(); - dataSource.resetDb(); putSomeKeyValue(dataSource); int limit = 2; @@ -350,8 +321,6 @@ public void getKeysNext() { for (int i = 0; i < limit; i++) { Assert.assertArrayEquals(list.get(i), seekKeyLimitNext.get(i)); } - - dataSource.resetDb(); dataSource.closeDB(); } @@ -359,9 +328,6 @@ public void getKeysNext() { public void prefixQueryTest() { LevelDbDataSourceImpl dataSource = new LevelDbDataSourceImpl( Args.getInstance().getOutputDirectory(), "test_prefixQuery"); - dataSource.initDB(); - dataSource.resetDb(); - putSomeKeyValue(dataSource); // put a kv that will not be queried. byte[] key7 = "0000001".getBytes(); @@ -387,16 +353,14 @@ public void prefixQueryTest() { Assert.assertEquals(list.size(), result.size()); list.forEach(entry -> Assert.assertTrue(result.contains(entry))); - dataSource.resetDb(); dataSource.closeDB(); } @Test public void initDbTest() { makeExceptionDb("test_initDb"); - LevelDbDataSourceImpl dataSource = new LevelDbDataSourceImpl( - Args.getInstance().getOutputDirectory(), "test_initDb"); - TronError thrown = assertThrows(TronError.class, dataSource::initDB); + TronError thrown = assertThrows(TronError.class, () -> new LevelDbDataSourceImpl( + Args.getInstance().getOutputDirectory(), "test_initDb")); assertEquals(TronError.ErrCode.LEVELDB_INIT, thrown.getErrCode()); } @@ -412,15 +376,12 @@ public void testCheckOrInitEngine() { LevelDbDataSourceImpl dataSource; dataSource = new LevelDbDataSourceImpl(dir, "test_engine"); - dataSource.initDB(); dataSource.closeDB(); - System.gc(); PropUtil.writeProperty(enginePath, "ENGINE", "ROCKSDB"); Assert.assertEquals("ROCKSDB", PropUtil.readProperty(enginePath, "ENGINE")); try { - dataSource = new LevelDbDataSourceImpl(dir, "test_engine"); - dataSource.initDB(); + new LevelDbDataSourceImpl(dir, "test_engine"); } catch (TronError e) { Assert.assertEquals("Cannot open ROCKSDB database with LEVELDB engine.", e.getMessage()); } @@ -433,20 +394,16 @@ public void testLevelDbOpenRocksDb() { .get(StorageUtils.getOutputDirectoryByDbName(name), CommonParameter .getInstance().getStorage().getDbDirectory()).toString(); RocksDbDataSourceImpl rocksDb = new RocksDbDataSourceImpl(output, name); - rocksDb.initDB(); rocksDb.putData(key1, value1); rocksDb.closeDB(); - LevelDbDataSourceImpl levelDB = - new LevelDbDataSourceImpl(StorageUtils.getOutputDirectoryByDbName(name), name); exception.expectMessage("Cannot open ROCKSDB database with LEVELDB engine."); - levelDB.initDB(); + new LevelDbDataSourceImpl(StorageUtils.getOutputDirectoryByDbName(name), name); } @Test public void testNewInstance() { dataSourceTest.closeDB(); LevelDbDataSourceImpl newInst = dataSourceTest.newInstance(); - newInst.initDB(); assertFalse(newInst.flush()); newInst.closeDB(); LevelDbDataSourceImpl empty = new LevelDbDataSourceImpl(); @@ -456,7 +413,6 @@ public void testNewInstance() { LevelDbDataSourceImpl newInst2 = new LevelDbDataSourceImpl( StorageUtils.getOutputDirectoryByDbName(name), name); - newInst2.initDB(); newInst2.closeDB(); } @@ -464,8 +420,6 @@ public void testNewInstance() { public void testGetNext() { LevelDbDataSourceImpl dataSource = new LevelDbDataSourceImpl( Args.getInstance().getOutputDirectory(), "test_getNext_key"); - dataSource.initDB(); - dataSource.resetDb(); putSomeKeyValue(dataSource); // case: normal Map seekKvLimitNext = dataSource.getNext("0000000300".getBytes(), 2); @@ -483,7 +437,6 @@ public void testGetNext() { // case: limit<=0 seekKvLimitNext = dataSource.getNext("0000000300".getBytes(), 0); Assert.assertEquals(0, seekKvLimitNext.size()); - dataSource.resetDb(); dataSource.closeDB(); } @@ -491,8 +444,6 @@ public void testGetNext() { public void testGetlatestValues() { LevelDbDataSourceImpl dataSource = new LevelDbDataSourceImpl( Args.getInstance().getOutputDirectory(), "test_getlatestValues_key"); - dataSource.initDB(); - dataSource.resetDb(); putSomeKeyValue(dataSource); // case: normal Set seekKeyLimitNext = dataSource.getlatestValues(2); @@ -503,14 +454,12 @@ public void testGetlatestValues() { // case: limit<=0 seekKeyLimitNext = dataSource.getlatestValues(0); assertEquals(0, seekKeyLimitNext.size()); - dataSource.resetDb(); dataSource.closeDB(); } private void makeExceptionDb(String dbName) { LevelDbDataSourceImpl dataSource = new LevelDbDataSourceImpl( Args.getInstance().getOutputDirectory(), "test_initDb"); - dataSource.initDB(); dataSource.closeDB(); FileUtil.saveData(dataSource.getDbPath().toString() + "/CURRENT", "...", Boolean.FALSE); diff --git a/framework/src/test/java/org/tron/common/storage/rocksdb/RocksDbDataSourceImplTest.java b/framework/src/test/java/org/tron/common/storage/rocksdb/RocksDbDataSourceImplTest.java index 3ccbb703710..bf71b024541 100644 --- a/framework/src/test/java/org/tron/common/storage/rocksdb/RocksDbDataSourceImplTest.java +++ b/framework/src/test/java/org/tron/common/storage/rocksdb/RocksDbDataSourceImplTest.java @@ -88,7 +88,6 @@ public void testPutGet() { dataSourceTest.resetDb(); String key1 = PublicMethod.getRandomPrivateKey(); byte[] key = key1.getBytes(); - dataSourceTest.initDB(); String value1 = "50000"; byte[] value = value1.getBytes(); @@ -130,8 +129,6 @@ public void testReset() { public void testupdateByBatchInner() { RocksDbDataSourceImpl dataSource = new RocksDbDataSourceImpl( Args.getInstance().getOutputDirectory(), "test_updateByBatch"); - dataSource.initDB(); - dataSource.resetDb(); String key1 = PublicMethod.getRandomPrivateKey(); String value1 = "50000"; String key2 = PublicMethod.getRandomPrivateKey(); @@ -170,7 +167,6 @@ public void testupdateByBatchInner() { public void testdeleteData() { RocksDbDataSourceImpl dataSource = new RocksDbDataSourceImpl( Args.getInstance().getOutputDirectory(), "test_delete"); - dataSource.initDB(); String key1 = PublicMethod.getRandomPrivateKey(); byte[] key = key1.getBytes(); dataSource.deleteData(key); @@ -184,8 +180,6 @@ public void testdeleteData() { public void testallKeys() { RocksDbDataSourceImpl dataSource = new RocksDbDataSourceImpl( Args.getInstance().getOutputDirectory(), "test_find_key"); - dataSource.initDB(); - dataSource.resetDb(); String key1 = PublicMethod.getRandomPrivateKey(); byte[] key = key1.getBytes(); @@ -202,13 +196,11 @@ public void testallKeys() { dataSource.putData(key2, value2); assertEquals(2, dataSource.allKeys().size()); - dataSource.resetDb(); dataSource.closeDB(); } @Test(timeout = 1000) public void testLockReleased() { - dataSourceTest.initDB(); // normal close dataSourceTest.closeDB(); // closing already closed db. @@ -223,8 +215,6 @@ public void testLockReleased() { public void allKeysTest() { RocksDbDataSourceImpl dataSource = new RocksDbDataSourceImpl( Args.getInstance().getOutputDirectory(), "test_allKeysTest_key"); - dataSource.initDB(); - dataSource.resetDb(); byte[] key = "0000000987b10fbb7f17110757321".getBytes(); byte[] value = "50000".getBytes(); @@ -237,7 +227,6 @@ public void allKeysTest() { logger.info(ByteArray.toStr(keyOne)); }); assertEquals(2, dataSource.allKeys().size()); - dataSource.resetDb(); dataSource.closeDB(); } @@ -263,31 +252,15 @@ private void putSomeKeyValue(RocksDbDataSourceImpl dataSource) { dataSource.putData(key4, value4); } - @Test - public void seekTest() { - RocksDbDataSourceImpl dataSource = new RocksDbDataSourceImpl( - Args.getInstance().getOutputDirectory(), "test_seek_key"); - dataSource.initDB(); - dataSource.resetDb(); - - putSomeKeyValue(dataSource); - Assert.assertTrue(true); - dataSource.resetDb(); - dataSource.closeDB(); - } - @Test public void getValuesNext() { RocksDbDataSourceImpl dataSource = new RocksDbDataSourceImpl( Args.getInstance().getOutputDirectory(), "test_getValuesNext_key"); - dataSource.initDB(); - dataSource.resetDb(); putSomeKeyValue(dataSource); Set seekKeyLimitNext = dataSource.getValuesNext("0000000300".getBytes(), 2); HashSet hashSet = Sets.newHashSet(ByteArray.toStr(value3), ByteArray.toStr(value4)); seekKeyLimitNext.forEach( value -> Assert.assertTrue("getValuesNext", hashSet.contains(ByteArray.toStr(value)))); - dataSource.resetDb(); dataSource.closeDB(); } @@ -303,21 +276,17 @@ public void testCheckOrInitEngine() { RocksDbDataSourceImpl dataSource; dataSource = new RocksDbDataSourceImpl(dir, "test_engine"); - dataSource.initDB(); Assert.assertNotNull(dataSource.getDatabase()); dataSource.closeDB(); - dataSource = null; - System.gc(); PropUtil.writeProperty(enginePath, "ENGINE", "LEVELDB"); Assert.assertEquals("LEVELDB", PropUtil.readProperty(enginePath, "ENGINE")); - dataSource = new RocksDbDataSourceImpl(dir, "test_engine"); + try { - dataSource.initDB(); + new RocksDbDataSourceImpl(dir, "test_engine"); } catch (TronError e) { Assert.assertEquals("Cannot open LEVELDB database with ROCKSDB engine.", e.getMessage()); } - Assert.assertNull(dataSource.getDatabase()); PropUtil.writeProperty(enginePath, "ENGINE", "ROCKSDB"); } @@ -325,8 +294,6 @@ public void testCheckOrInitEngine() { public void testGetNext() { RocksDbDataSourceImpl dataSource = new RocksDbDataSourceImpl( Args.getInstance().getOutputDirectory(), "test_getNext_key"); - dataSource.initDB(); - dataSource.resetDb(); putSomeKeyValue(dataSource); // case: normal Map seekKvLimitNext = dataSource.getNext("0000000300".getBytes(), 2); @@ -344,7 +311,6 @@ public void testGetNext() { // case: limit<=0 seekKvLimitNext = dataSource.getNext("0000000300".getBytes(), 0); Assert.assertEquals(0, seekKvLimitNext.size()); - dataSource.resetDb(); dataSource.closeDB(); } @@ -352,8 +318,6 @@ public void testGetNext() { public void testGetlatestValues() { RocksDbDataSourceImpl dataSource = new RocksDbDataSourceImpl( Args.getInstance().getOutputDirectory(), "test_getlatestValues_key"); - dataSource.initDB(); - dataSource.resetDb(); putSomeKeyValue(dataSource); // case: normal Set seekKeyLimitNext = dataSource.getlatestValues(2); @@ -364,7 +328,6 @@ public void testGetlatestValues() { // case: limit<=0 seekKeyLimitNext = dataSource.getlatestValues(0); assertEquals(0, seekKeyLimitNext.size()); - dataSource.resetDb(); dataSource.closeDB(); } @@ -372,8 +335,6 @@ public void testGetlatestValues() { public void getKeysNext() { RocksDbDataSourceImpl dataSource = new RocksDbDataSourceImpl( Args.getInstance().getOutputDirectory(), "test_getKeysNext_key"); - dataSource.initDB(); - dataSource.resetDb(); putSomeKeyValue(dataSource); int limit = 2; @@ -383,8 +344,6 @@ public void getKeysNext() { for (int i = 0; i < limit; i++) { Assert.assertArrayEquals(list.get(i), seekKeyLimitNext.get(i)); } - - dataSource.resetDb(); dataSource.closeDB(); } @@ -392,8 +351,6 @@ public void getKeysNext() { public void prefixQueryTest() { RocksDbDataSourceImpl dataSource = new RocksDbDataSourceImpl( Args.getInstance().getOutputDirectory(), "test_prefixQuery"); - dataSource.initDB(); - dataSource.resetDb(); putSomeKeyValue(dataSource); // put a kv that will not be queried. @@ -420,16 +377,14 @@ public void prefixQueryTest() { Assert.assertEquals(list.size(), result.size()); list.forEach(entry -> Assert.assertTrue(result.contains(entry))); - dataSource.resetDb(); dataSource.closeDB(); } @Test public void initDbTest() { makeExceptionDb("test_initDb"); - RocksDbDataSourceImpl dataSource = new RocksDbDataSourceImpl( - Args.getInstance().getOutputDirectory(), "test_initDb"); - TronError thrown = assertThrows(TronError.class, dataSource::initDB); + TronError thrown = assertThrows(TronError.class, () -> new RocksDbDataSourceImpl( + Args.getInstance().getOutputDirectory(), "test_initDb")); assertEquals(TronError.ErrCode.ROCKSDB_INIT, thrown.getErrCode()); } @@ -441,12 +396,10 @@ public void testRocksDbOpenLevelDb() { .getInstance().getStorage().getDbDirectory()).toString(); LevelDbDataSourceImpl levelDb = new LevelDbDataSourceImpl( StorageUtils.getOutputDirectoryByDbName(name), name); - levelDb.initDB(); levelDb.putData(key1, value1); levelDb.closeDB(); - RocksDbDataSourceImpl rocksDb = new RocksDbDataSourceImpl(output, name); expectedException.expectMessage("Cannot open LEVELDB database with ROCKSDB engine."); - rocksDb.initDB(); + new RocksDbDataSourceImpl(output, name); } @Test @@ -457,26 +410,23 @@ public void testRocksDbOpenLevelDb2() { .getInstance().getStorage().getDbDirectory()).toString(); LevelDbDataSourceImpl levelDb = new LevelDbDataSourceImpl( StorageUtils.getOutputDirectoryByDbName(name), name); - levelDb.initDB(); levelDb.putData(key1, value1); levelDb.closeDB(); // delete engine.properties file to simulate the case that db.engine is not set. - File engineFile = Paths - .get(output, name, "engine.properties").toFile(); + File engineFile = Paths.get(output, name, "engine.properties").toFile(); if (engineFile.exists()) { engineFile.delete(); } Assert.assertFalse(engineFile.exists()); - RocksDbDataSourceImpl rocksDb = new RocksDbDataSourceImpl(output, name); + expectedException.expectMessage("Cannot open LEVELDB database with ROCKSDB engine."); - rocksDb.initDB(); + new RocksDbDataSourceImpl(output, name); } @Test public void testNewInstance() { dataSourceTest.closeDB(); RocksDbDataSourceImpl newInst = dataSourceTest.newInstance(); - newInst.initDB(); assertFalse(newInst.flush()); newInst.closeDB(); RocksDbDataSourceImpl empty = new RocksDbDataSourceImpl(); @@ -486,7 +436,6 @@ public void testNewInstance() { .get(StorageUtils.getOutputDirectoryByDbName("newInst2"), CommonParameter .getInstance().getStorage().getDbDirectory()).toString(); RocksDbDataSourceImpl newInst2 = new RocksDbDataSourceImpl(output, "newInst2"); - newInst2.initDB(); newInst2.closeDB(); } @@ -494,7 +443,6 @@ public void testNewInstance() { public void backupAndDelete() throws RocksDBException { RocksDbDataSourceImpl dataSource = new RocksDbDataSourceImpl( Args.getInstance().getOutputDirectory(), "backupAndDelete"); - dataSource.initDB(); putSomeKeyValue(dataSource); Path dir = Paths.get(Args.getInstance().getOutputDirectory(), "backup"); String path = dir + File.separator; @@ -511,8 +459,6 @@ public void backupAndDelete() throws RocksDBException { public void testGetTotal() { RocksDbDataSourceImpl dataSource = new RocksDbDataSourceImpl( Args.getInstance().getOutputDirectory(), "test_getTotal_key"); - dataSource.initDB(); - dataSource.resetDb(); Map dataMapset = Maps.newHashMap(); dataMapset.put(key1, value1); @@ -520,14 +466,12 @@ public void testGetTotal() { dataMapset.put(key3, value3); dataMapset.forEach(dataSource::putData); Assert.assertEquals(dataMapset.size(), dataSource.getTotal()); - dataSource.resetDb(); dataSource.closeDB(); } private void makeExceptionDb(String dbName) { RocksDbDataSourceImpl dataSource = new RocksDbDataSourceImpl( Args.getInstance().getOutputDirectory(), "test_initDb"); - dataSource.initDB(); dataSource.closeDB(); FileUtil.saveData(dataSource.getDbPath().toString() + "/CURRENT", "...", Boolean.FALSE); diff --git a/framework/src/test/java/org/tron/core/db2/ChainbaseTest.java b/framework/src/test/java/org/tron/core/db2/ChainbaseTest.java index ee9813be633..4ab36ad2c88 100644 --- a/framework/src/test/java/org/tron/core/db2/ChainbaseTest.java +++ b/framework/src/test/java/org/tron/core/db2/ChainbaseTest.java @@ -76,9 +76,7 @@ public void initDb() throws IOException { public void testPrefixQueryForLeveldb() { LevelDbDataSourceImpl dataSource = new LevelDbDataSourceImpl( Args.getInstance().getOutputDirectory(), "testPrefixQueryForLeveldb"); - dataSource.initDB(); - this.chainbase = new Chainbase(new SnapshotRoot( - new LevelDB(dataSource))); + this.chainbase = new Chainbase(new SnapshotRoot(new LevelDB(dataSource))); testDb(chainbase); testRoot(dataSource); chainbase.reset(); @@ -89,7 +87,6 @@ public void testPrefixQueryForLeveldb() { public void testPrefixQueryForRocksdb() { RocksDbDataSourceImpl dataSource = new RocksDbDataSourceImpl( Args.getInstance().getOutputDirectory(), "testPrefixQueryForRocksdb"); - dataSource.initDB(); this.chainbase = new Chainbase(new SnapshotRoot( new org.tron.core.db2.common.RocksDB(dataSource))); testDb(chainbase); diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index baad667c071..8be6c6626f4 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -2182,12 +2182,12 @@ - - - + + + - - + + From 7015fa341e8a561d8d4c61cb9b86b109489af71c Mon Sep 17 00:00:00 2001 From: 317787106 <317787106@qq.com> Date: Tue, 9 Sep 2025 18:30:29 +0800 Subject: [PATCH 45/59] fix(test): remove all junit dir and add timeout for gRPC call (#6441) * remove all junit dir under tmp * add withDeadlineAfter for newBlockingStub * use TimeoutInterceptor for each grpc call * optimize SyncBlockChainMsgHandlerTest --- .../tron/common/utils/TimeoutInterceptor.java | 30 ++++++++++++ .../tron/core/event/BlockEventGetTest.java | 46 +++++++++++-------- .../SyncBlockChainMsgHandlerTest.java | 41 +++++++++++------ .../core/services/RpcApiServicesTest.java | 4 ++ .../org/tron/core/services/WalletApiTest.java | 5 +- .../LiteFnQueryGrpcInterceptorTest.java | 18 +++++--- .../filter/RpcApiAccessInterceptorTest.java | 5 ++ .../java/org/tron/plugins/DbLiteTest.java | 22 ++++----- 8 files changed, 121 insertions(+), 50 deletions(-) create mode 100644 common/src/main/java/org/tron/common/utils/TimeoutInterceptor.java diff --git a/common/src/main/java/org/tron/common/utils/TimeoutInterceptor.java b/common/src/main/java/org/tron/common/utils/TimeoutInterceptor.java new file mode 100644 index 00000000000..07b00e861e8 --- /dev/null +++ b/common/src/main/java/org/tron/common/utils/TimeoutInterceptor.java @@ -0,0 +1,30 @@ +package org.tron.common.utils; + +import io.grpc.CallOptions; +import io.grpc.Channel; +import io.grpc.ClientCall; +import io.grpc.ClientInterceptor; +import io.grpc.MethodDescriptor; +import java.util.concurrent.TimeUnit; + + +public class TimeoutInterceptor implements ClientInterceptor { + + private final long timeout; + + /** + * @param timeout ms + */ + public TimeoutInterceptor(long timeout) { + this.timeout = timeout; + } + + @Override + public ClientCall interceptCall( + MethodDescriptor method, + CallOptions callOptions, + Channel next) { + return next.newCall(method, + callOptions.withDeadlineAfter(timeout, TimeUnit.MILLISECONDS)); + } +} diff --git a/framework/src/test/java/org/tron/core/event/BlockEventGetTest.java b/framework/src/test/java/org/tron/core/event/BlockEventGetTest.java index b6835cfcf82..25215ce4a32 100644 --- a/framework/src/test/java/org/tron/core/event/BlockEventGetTest.java +++ b/framework/src/test/java/org/tron/core/event/BlockEventGetTest.java @@ -11,9 +11,10 @@ import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import lombok.extern.slf4j.Slf4j; -import org.junit.After; +import org.junit.AfterClass; import org.junit.Assert; import org.junit.Before; +import org.junit.BeforeClass; import org.junit.ClassRule; import org.junit.Test; import org.junit.rules.TemporaryFolder; @@ -63,26 +64,33 @@ public class BlockEventGetTest extends BlockGenerate { protected Manager dbManager; long currentHeader = -1; private TronNetDelegate tronNetDelegate; - private TronApplicationContext context; + private static TronApplicationContext context; static LocalDateTime localDateTime = LocalDateTime.now(); private long time = ZonedDateTime.of(localDateTime, ZoneId.systemDefault()).toInstant().toEpochMilli(); - protected void initDbPath() throws IOException { - dbPath = temporaryFolder.newFolder().toString(); + + public static String dbPath() { + try { + return temporaryFolder.newFolder().toString(); + } catch (IOException e) { + Assert.fail("create temp folder failed"); + } + return null; + } + + @BeforeClass + public static void init() { + Args.setParam(new String[] {"--output-directory", dbPath()}, Constant.TEST_CONF); + context = new TronApplicationContext(DefaultConfig.class); } @Before public void before() throws IOException { - initDbPath(); - logger.info("Full node running."); - Args.setParam(new String[] {"-d", dbPath}, Constant.TEST_CONF); Args.getInstance().setNodeListenPort(10000 + port.incrementAndGet()); - context = new TronApplicationContext(DefaultConfig.class); - dbManager = context.getBean(Manager.class); setManager(dbManager); @@ -91,7 +99,7 @@ public void before() throws IOException { tronNetDelegate = context.getBean(TronNetDelegate.class); tronNetDelegate.setExit(false); currentHeader = dbManager.getDynamicPropertiesStore() - .getLatestBlockHeaderNumberFromDB(); + .getLatestBlockHeaderNumberFromDB(); ByteString addressBS = ByteString.copyFrom(address); WitnessCapsule witnessCapsule = new WitnessCapsule(addressBS); @@ -108,8 +116,10 @@ public void before() throws IOException { dps.saveAllowTvmShangHai(1); } - @After - public void after() throws IOException { + @AfterClass + public static void after() throws IOException { + context.destroy(); + Args.clearParam(); } @Test @@ -132,13 +142,13 @@ public void test() throws Exception { + "57c973388f044038eff0e6474425b38037e75e66d6b3047647290605449c7764736f6c63430008140033"; Protocol.Transaction trx = TvmTestUtils.generateDeploySmartContractAndGetTransaction( "TestTRC20", address, "[{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\"" - + ":\"from\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"to\",\"type\":\"address\"}" - + ",{\"indexed\":false,\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"Transfer\"," - + "\"type\":\"event\"}]", code, 0, (long) 1e9, 100, null, 1); + + ":\"from\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"to\",\"type\"" + + ":\"address\"},{\"indexed\":false,\"name\":\"value\",\"type\":\"uint256\"}],\"name\"" + + ":\"Transfer\",\"type\":\"event\"}]", code, 0, (long) 1e9, 100, null, 1); trx = trx.toBuilder().addRet( - Protocol.Transaction.Result.newBuilder() - .setContractRetValue(Protocol.Transaction.Result.contractResult.SUCCESS_VALUE) - .build()).build(); + Protocol.Transaction.Result.newBuilder() + .setContractRetValue(Protocol.Transaction.Result.contractResult.SUCCESS_VALUE) + .build()).build(); Protocol.Block block = getSignedBlock(witnessCapsule.getAddress(), time, privateKey); BlockCapsule blockCapsule = new BlockCapsule(block.toBuilder().addTransactions(trx).build()); diff --git a/framework/src/test/java/org/tron/core/net/messagehandler/SyncBlockChainMsgHandlerTest.java b/framework/src/test/java/org/tron/core/net/messagehandler/SyncBlockChainMsgHandlerTest.java index eccab2aeb00..0fc784dd852 100644 --- a/framework/src/test/java/org/tron/core/net/messagehandler/SyncBlockChainMsgHandlerTest.java +++ b/framework/src/test/java/org/tron/core/net/messagehandler/SyncBlockChainMsgHandlerTest.java @@ -1,15 +1,17 @@ package org.tron.core.net.messagehandler; +import java.io.IOException; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.InetSocketAddress; import java.util.ArrayList; import java.util.List; -import org.junit.After; +import org.junit.AfterClass; import org.junit.Assert; import org.junit.Before; -import org.junit.Rule; +import org.junit.BeforeClass; +import org.junit.ClassRule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.tron.common.application.TronApplicationContext; @@ -26,17 +28,29 @@ public class SyncBlockChainMsgHandlerTest { - private TronApplicationContext context; + private static TronApplicationContext context; private SyncBlockChainMsgHandler handler; private PeerConnection peer; - @Rule - public TemporaryFolder temporaryFolder = new TemporaryFolder(); + @ClassRule + public static final TemporaryFolder temporaryFolder = new TemporaryFolder(); + + public static String dbPath() { + try { + return temporaryFolder.newFolder().toString(); + } catch (IOException e) { + Assert.fail("create temp folder failed"); + } + return null; + } + + @BeforeClass + public static void before() { + Args.setParam(new String[] {"--output-directory", dbPath()}, Constant.TEST_CONF); + context = new TronApplicationContext(DefaultConfig.class); + } @Before public void init() throws Exception { - Args.setParam(new String[]{"--output-directory", - temporaryFolder.newFolder().toString(), "--debug"}, Constant.TEST_CONF); - context = new TronApplicationContext(DefaultConfig.class); handler = context.getBean(SyncBlockChainMsgHandler.class); peer = context.getBean(PeerConnection.class); Channel c1 = new Channel(); @@ -65,16 +79,16 @@ public void testProcessMessage() throws Exception { blockIds.add(new BlockCapsule.BlockId()); SyncBlockChainMessage message = new SyncBlockChainMessage(blockIds); Method method = handler.getClass().getDeclaredMethod( - "check", PeerConnection.class, SyncBlockChainMessage.class); + "check", PeerConnection.class, SyncBlockChainMessage.class); method.setAccessible(true); - boolean f = (boolean)method.invoke(handler, peer, message); + boolean f = (boolean) method.invoke(handler, peer, message); Assert.assertNotNull(message.getAnswerMessage()); Assert.assertNotNull(message.toString()); Assert.assertNotNull(((BlockInventoryMessage) message).getAnswerMessage()); Assert.assertFalse(f); method.invoke(handler, peer, message); method.invoke(handler, peer, message); - f = (boolean)method.invoke(handler, peer, message); + f = (boolean) method.invoke(handler, peer, message); Assert.assertFalse(f); Method method1 = handler.getClass().getDeclaredMethod( @@ -93,8 +107,9 @@ public void testProcessMessage() throws Exception { Assert.assertEquals(1, list.size()); } - @After - public void destroy() { + @AfterClass + public static void destroy() { + context.destroy(); Args.clearParam(); } diff --git a/framework/src/test/java/org/tron/core/services/RpcApiServicesTest.java b/framework/src/test/java/org/tron/core/services/RpcApiServicesTest.java index 419a875df82..f98e0b03a57 100644 --- a/framework/src/test/java/org/tron/core/services/RpcApiServicesTest.java +++ b/framework/src/test/java/org/tron/core/services/RpcApiServicesTest.java @@ -53,6 +53,7 @@ import org.tron.common.utils.ByteArray; import org.tron.common.utils.PublicMethod; import org.tron.common.utils.Sha256Hash; +import org.tron.common.utils.TimeoutInterceptor; import org.tron.core.Constant; import org.tron.core.Wallet; import org.tron.core.capsule.AccountCapsule; @@ -159,12 +160,15 @@ public static void init() throws IOException { channelFull = ManagedChannelBuilder.forTarget(fullNode) .usePlaintext() + .intercept(new TimeoutInterceptor(5000)) .build(); channelPBFT = ManagedChannelBuilder.forTarget(pBFTNode) .usePlaintext() + .intercept(new TimeoutInterceptor(5000)) .build(); channelSolidity = ManagedChannelBuilder.forTarget(solidityNode) .usePlaintext() + .intercept(new TimeoutInterceptor(5000)) .build(); context = new TronApplicationContext(DefaultConfig.class); databaseBlockingStubFull = DatabaseGrpc.newBlockingStub(channelFull); diff --git a/framework/src/test/java/org/tron/core/services/WalletApiTest.java b/framework/src/test/java/org/tron/core/services/WalletApiTest.java index ebae038ab5e..5b548b3032a 100644 --- a/framework/src/test/java/org/tron/core/services/WalletApiTest.java +++ b/framework/src/test/java/org/tron/core/services/WalletApiTest.java @@ -2,6 +2,7 @@ import io.grpc.ManagedChannelBuilder; import java.io.IOException; +import java.util.concurrent.TimeUnit; import lombok.extern.slf4j.Slf4j; import org.junit.AfterClass; import org.junit.Assert; @@ -15,6 +16,7 @@ import org.tron.common.application.ApplicationFactory; import org.tron.common.application.TronApplicationContext; import org.tron.common.utils.PublicMethod; +import org.tron.common.utils.TimeoutInterceptor; import org.tron.core.Constant; import org.tron.core.config.DefaultConfig; import org.tron.core.config.args.Args; @@ -32,7 +34,7 @@ public class WalletApiTest { @BeforeClass public static void init() throws IOException { - Args.setParam(new String[]{ "-d", temporaryFolder.newFolder().toString(), + Args.setParam(new String[] {"-d", temporaryFolder.newFolder().toString(), "--p2p-disable", "true"}, Constant.TEST_CONF); Args.getInstance().setRpcPort(PublicMethod.chooseRandomPort()); Args.getInstance().setRpcEnable(true); @@ -47,6 +49,7 @@ public void listNodesTest() { Args.getInstance().getRpcPort()); io.grpc.ManagedChannel channel = ManagedChannelBuilder.forTarget(fullNode) .usePlaintext() + .intercept(new TimeoutInterceptor(5000)) .build(); try { WalletGrpc.WalletBlockingStub walletStub = WalletGrpc.newBlockingStub(channel); diff --git a/framework/src/test/java/org/tron/core/services/filter/LiteFnQueryGrpcInterceptorTest.java b/framework/src/test/java/org/tron/core/services/filter/LiteFnQueryGrpcInterceptorTest.java index d98eb9b5384..6694ddf08df 100644 --- a/framework/src/test/java/org/tron/core/services/filter/LiteFnQueryGrpcInterceptorTest.java +++ b/framework/src/test/java/org/tron/core/services/filter/LiteFnQueryGrpcInterceptorTest.java @@ -21,6 +21,7 @@ import org.tron.common.application.ApplicationFactory; import org.tron.common.application.TronApplicationContext; import org.tron.common.utils.PublicMethod; +import org.tron.common.utils.TimeoutInterceptor; import org.tron.core.ChainBaseManager; import org.tron.core.Constant; import org.tron.core.config.DefaultConfig; @@ -51,7 +52,7 @@ public class LiteFnQueryGrpcInterceptorTest { */ @BeforeClass public static void init() throws IOException { - Args.setParam(new String[]{"-d", temporaryFolder.newFolder().toString()}, Constant.TEST_CONF); + Args.setParam(new String[] {"-d", temporaryFolder.newFolder().toString()}, Constant.TEST_CONF); Args.getInstance().setRpcEnable(true); Args.getInstance().setRpcPort(PublicMethod.chooseRandomPort()); Args.getInstance().setRpcSolidityEnable(true); @@ -60,20 +61,23 @@ public static void init() throws IOException { Args.getInstance().setRpcOnPBFTPort(PublicMethod.chooseRandomPort()); Args.getInstance().setP2pDisable(true); String fullnode = String.format("%s:%d", Constant.LOCAL_HOST, - Args.getInstance().getRpcPort()); + Args.getInstance().getRpcPort()); String solidityNode = String.format("%s:%d", Constant.LOCAL_HOST, - Args.getInstance().getRpcOnSolidityPort()); + Args.getInstance().getRpcOnSolidityPort()); String pBFTNode = String.format("%s:%d", Constant.LOCAL_HOST, Args.getInstance().getRpcOnPBFTPort()); channelFull = ManagedChannelBuilder.forTarget(fullnode) - .usePlaintext() - .build(); + .usePlaintext() + .intercept(new TimeoutInterceptor(5000)) + .build(); channelSolidity = ManagedChannelBuilder.forTarget(solidityNode) .usePlaintext() + .intercept(new TimeoutInterceptor(5000)) .build(); channelpBFT = ManagedChannelBuilder.forTarget(pBFTNode) - .usePlaintext() - .build(); + .usePlaintext() + .intercept(new TimeoutInterceptor(5000)) + .build(); context = new TronApplicationContext(DefaultConfig.class); blockingStubFull = WalletGrpc.newBlockingStub(channelFull); blockingStubSolidity = WalletSolidityGrpc.newBlockingStub(channelSolidity); diff --git a/framework/src/test/java/org/tron/core/services/filter/RpcApiAccessInterceptorTest.java b/framework/src/test/java/org/tron/core/services/filter/RpcApiAccessInterceptorTest.java index 9b48f0ffb98..ced05c7c5b3 100644 --- a/framework/src/test/java/org/tron/core/services/filter/RpcApiAccessInterceptorTest.java +++ b/framework/src/test/java/org/tron/core/services/filter/RpcApiAccessInterceptorTest.java @@ -14,6 +14,7 @@ import java.util.Collections; import java.util.List; import java.util.Objects; +import java.util.concurrent.TimeUnit; import lombok.extern.slf4j.Slf4j; import org.junit.AfterClass; import org.junit.BeforeClass; @@ -32,6 +33,7 @@ import org.tron.common.application.ApplicationFactory; import org.tron.common.application.TronApplicationContext; import org.tron.common.utils.PublicMethod; +import org.tron.common.utils.TimeoutInterceptor; import org.tron.core.Constant; import org.tron.core.config.DefaultConfig; import org.tron.core.config.args.Args; @@ -73,12 +75,15 @@ public static void init() throws IOException { channelFull = ManagedChannelBuilder.forTarget(fullNode) .usePlaintext() + .intercept(new TimeoutInterceptor(5000)) .build(); channelPBFT = ManagedChannelBuilder.forTarget(pBFTNode) .usePlaintext() + .intercept(new TimeoutInterceptor(5000)) .build(); channelSolidity = ManagedChannelBuilder.forTarget(solidityNode) .usePlaintext() + .intercept(new TimeoutInterceptor(5000)) .build(); context = new TronApplicationContext(DefaultConfig.class); diff --git a/plugins/src/test/java/org/tron/plugins/DbLiteTest.java b/plugins/src/test/java/org/tron/plugins/DbLiteTest.java index 47494812ef3..80fd90cce81 100644 --- a/plugins/src/test/java/org/tron/plugins/DbLiteTest.java +++ b/plugins/src/test/java/org/tron/plugins/DbLiteTest.java @@ -7,10 +7,8 @@ import java.io.File; import java.io.IOException; import java.nio.file.Paths; -import java.util.concurrent.TimeUnit; import lombok.extern.slf4j.Slf4j; import org.junit.After; -import org.junit.ClassRule; import org.junit.Rule; import org.junit.rules.TemporaryFolder; import org.tron.api.WalletGrpc; @@ -21,6 +19,7 @@ import org.tron.common.crypto.ECKey; import org.tron.common.utils.FileUtil; import org.tron.common.utils.PublicMethod; +import org.tron.common.utils.TimeoutInterceptor; import org.tron.common.utils.Utils; import org.tron.core.config.DefaultConfig; import org.tron.core.config.args.Args; @@ -53,6 +52,7 @@ public void startApp() { Args.getInstance().getRpcPort()); channelFull = ManagedChannelBuilder.forTarget(fullNode) .usePlaintext() + .intercept(new TimeoutInterceptor(5000)) .build(); blockingStubFull = WalletGrpc.newBlockingStub(channelFull); } @@ -69,7 +69,7 @@ public void shutdown() throws InterruptedException { public void init(String dbType) throws IOException { dbPath = folder.newFolder().toString(); - Args.setParam(new String[]{ + Args.setParam(new String[] { "-d", dbPath, "-w", "--p2p-disable", "true", "--storage-db-engine", dbType}, "config-localtest.conf"); // allow account root @@ -92,15 +92,15 @@ public void testTools(String dbType, int checkpointVersion) dbPath = String.format("%s_%s_%d", dbPath, dbType, System.currentTimeMillis()); init(dbType); final String[] argsForSnapshot = - new String[]{"-o", "split", "-t", "snapshot", "--fn-data-path", + new String[] {"-o", "split", "-t", "snapshot", "--fn-data-path", dbPath + File.separator + databaseDir, "--dataset-path", dbPath}; final String[] argsForHistory = - new String[]{"-o", "split", "-t", "history", "--fn-data-path", + new String[] {"-o", "split", "-t", "history", "--fn-data-path", dbPath + File.separator + databaseDir, "--dataset-path", dbPath}; final String[] argsForMerge = - new String[]{"-o", "merge", "--fn-data-path", dbPath + File.separator + databaseDir, + new String[] {"-o", "merge", "--fn-data-path", dbPath + File.separator + databaseDir, "--dataset-path", dbPath + File.separator + "history"}; Args.getInstance().getStorage().setCheckpointVersion(checkpointVersion); DbLite.setRecentBlks(3); @@ -126,15 +126,15 @@ public void testTools(String dbType, int checkpointVersion) File database = new File(Paths.get(dbPath, databaseDir).toString()); if (!database.renameTo(new File(Paths.get(dbPath, databaseDir + "_bak").toString()))) { throw new RuntimeException( - String.format("rename %s to %s failed", database.getPath(), - Paths.get(dbPath, databaseDir))); + String.format("rename %s to %s failed", database.getPath(), + Paths.get(dbPath, databaseDir))); } // change snapshot to the new database File snapshot = new File(Paths.get(dbPath, "snapshot").toString()); if (!snapshot.renameTo(new File(Paths.get(dbPath, databaseDir).toString()))) { throw new RuntimeException( - String.format("rename snapshot to %s failed", - Paths.get(dbPath, databaseDir))); + String.format("rename snapshot to %s failed", + Paths.get(dbPath, databaseDir))); } // start and validate the snapshot startApp(); @@ -161,7 +161,7 @@ private void generateSomeTransactions(int during) { String sunPri = getRandomPrivateKey(); byte[] sunAddress = PublicMethod.getFinalAddress(sunPri); PublicMethod.sendcoin(address, 1L, - sunAddress, sunPri, blockingStubFull); + sunAddress, sunPri, blockingStubFull); try { Thread.sleep(sleepOnce); } catch (InterruptedException e) { From ba5f70e6376e4af2e2b2f5277b966207e0f3c0f3 Mon Sep 17 00:00:00 2001 From: 317787106 <317787106@qq.com> Date: Wed, 10 Sep 2025 15:08:31 +0800 Subject: [PATCH 46/59] update version to 4.8.1 (#6445) --- framework/src/main/java/org/tron/program/Version.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/src/main/java/org/tron/program/Version.java b/framework/src/main/java/org/tron/program/Version.java index 4e9528ee50e..bd19cc8e971 100644 --- a/framework/src/main/java/org/tron/program/Version.java +++ b/framework/src/main/java/org/tron/program/Version.java @@ -4,7 +4,7 @@ public class Version { public static final String VERSION_NAME = "GreatVoyage-v4.7.7-243-gb3555dd655"; public static final String VERSION_CODE = "18631"; - private static final String VERSION = "4.8.0"; + private static final String VERSION = "4.8.1"; public static String getVersion() { return VERSION; From 68773e3d5cbd2e0c0c96fbcce0a3a894aed2a915 Mon Sep 17 00:00:00 2001 From: halibobo1205 <82020050+halibobo1205@users.noreply.github.com> Date: Mon, 15 Sep 2025 09:40:27 +0800 Subject: [PATCH 47/59] feat(cli): change newly added "--keystore" parameter to "--keystore-factory" (#6446) The "--keystore" parameter name could be misleading, as it typically refers to encrypted key storage files or wallets, while this parameter actually runs a KeystoreFactory. The new name "--keystore-factory" makes the parameter's purpose more explicit and avoids potential confusion. --- .../main/java/org/tron/common/parameter/CommonParameter.java | 4 ++-- framework/src/main/java/org/tron/core/config/args/Args.java | 2 +- framework/src/main/java/org/tron/program/FullNode.java | 2 +- .../src/test/java/org/tron/core/config/args/ArgsTest.java | 5 +++-- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/common/src/main/java/org/tron/common/parameter/CommonParameter.java b/common/src/main/java/org/tron/common/parameter/CommonParameter.java index c70775cc8f4..4a2b8bc5f4c 100644 --- a/common/src/main/java/org/tron/common/parameter/CommonParameter.java +++ b/common/src/main/java/org/tron/common/parameter/CommonParameter.java @@ -211,8 +211,8 @@ public class CommonParameter { //If you are running KeystoreFactory, this flag is set to true @Getter @Setter - @Parameter(names = {"--keystore"}, description = "running KeystoreFactory") - public boolean keystore = false; + @Parameter(names = {"--keystore-factory"}, description = "running KeystoreFactory") + public boolean keystoreFactory = false; @Getter @Setter diff --git a/framework/src/main/java/org/tron/core/config/args/Args.java b/framework/src/main/java/org/tron/core/config/args/Args.java index b2972e950b2..70de1b5d443 100644 --- a/framework/src/main/java/org/tron/core/config/args/Args.java +++ b/framework/src/main/java/org/tron/core/config/args/Args.java @@ -168,7 +168,7 @@ public static void clearParam() { PARAMETER.tcpNettyWorkThreadNum = 0; PARAMETER.udpNettyWorkThreadNum = 0; PARAMETER.solidityNode = false; - PARAMETER.keystore = false; + PARAMETER.keystoreFactory = false; PARAMETER.trustNodeAddr = ""; PARAMETER.walletExtensionApi = false; PARAMETER.estimateEnergy = false; diff --git a/framework/src/main/java/org/tron/program/FullNode.java b/framework/src/main/java/org/tron/program/FullNode.java index e17ce8c8531..9f2f497a579 100644 --- a/framework/src/main/java/org/tron/program/FullNode.java +++ b/framework/src/main/java/org/tron/program/FullNode.java @@ -30,7 +30,7 @@ public static void main(String[] args) { SolidityNode.start(); return; } - if (parameter.isKeystore()) { + if (parameter.isKeystoreFactory()) { KeystoreFactory.start(); return; } diff --git a/framework/src/test/java/org/tron/core/config/args/ArgsTest.java b/framework/src/test/java/org/tron/core/config/args/ArgsTest.java index 7ad15580266..fb19528b626 100644 --- a/framework/src/test/java/org/tron/core/config/args/ArgsTest.java +++ b/framework/src/test/java/org/tron/core/config/args/ArgsTest.java @@ -57,7 +57,8 @@ public void destroy() { @Test public void get() { - Args.setParam(new String[] {"-c", Constant.TEST_CONF, "--keystore"}, Constant.TESTNET_CONF); + Args.setParam(new String[] {"-c", Constant.TEST_CONF, "--keystore-factory"}, + Constant.TESTNET_CONF); CommonParameter parameter = Args.getInstance(); @@ -128,7 +129,7 @@ public void get() { ByteArray.toHexString(Args.getLocalWitnesses() .getWitnessAccountAddress())); - Assert.assertTrue(parameter.isKeystore()); + Assert.assertTrue(parameter.isKeystoreFactory()); } @Test From 40cc590f89f03dd1dd037a80dea9e0fff94b6771 Mon Sep 17 00:00:00 2001 From: Aiden3885 Date: Mon, 15 Sep 2025 14:43:09 +0800 Subject: [PATCH 48/59] feat(tvm): remove deprecated config in TIP-6780 implementation (#6448) * feat(tvm): remove allowtvmselfdestructRestriction from commonParameter * feat(test): remove test for allowtvmselfdestructRestriction commonParameter * feat(tvm): remove constant COMMITTEE_ALLOW_TVM_SELFDESTRUCT_RESTRICTION --- .../java/org/tron/core/store/DynamicPropertiesStore.java | 2 +- .../main/java/org/tron/common/parameter/CommonParameter.java | 4 ---- common/src/main/java/org/tron/core/Constant.java | 3 --- framework/src/main/java/org/tron/core/config/args/Args.java | 5 ----- framework/src/test/java/org/tron/common/ParameterTest.java | 2 -- 5 files changed, 1 insertion(+), 15 deletions(-) diff --git a/chainbase/src/main/java/org/tron/core/store/DynamicPropertiesStore.java b/chainbase/src/main/java/org/tron/core/store/DynamicPropertiesStore.java index 91e8723bd28..89c5ba18e59 100644 --- a/chainbase/src/main/java/org/tron/core/store/DynamicPropertiesStore.java +++ b/chainbase/src/main/java/org/tron/core/store/DynamicPropertiesStore.java @@ -2957,7 +2957,7 @@ public long getAllowTvmSelfdestructRestriction() { return Optional.ofNullable(getUnchecked(ALLOW_TVM_SELFDESTRUCT_RESTRICTION)) .map(BytesCapsule::getData) .map(ByteArray::toLong) - .orElse(CommonParameter.getInstance().getAllowTvmSelfdestructRestriction()); + .orElse(0L); } public void saveAllowTvmSelfdestructRestriction(long value) { diff --git a/common/src/main/java/org/tron/common/parameter/CommonParameter.java b/common/src/main/java/org/tron/common/parameter/CommonParameter.java index 4a2b8bc5f4c..a602a660f8f 100644 --- a/common/src/main/java/org/tron/common/parameter/CommonParameter.java +++ b/common/src/main/java/org/tron/common/parameter/CommonParameter.java @@ -750,10 +750,6 @@ public class CommonParameter { @Setter public long allowTvmBlob; - @Getter - @Setter - public long allowTvmSelfdestructRestriction; - private static double calcMaxTimeRatio() { //return max(2.0, min(5.0, 5 * 4.0 / max(Runtime.getRuntime().availableProcessors(), 1))); return 5.0; diff --git a/common/src/main/java/org/tron/core/Constant.java b/common/src/main/java/org/tron/core/Constant.java index 5b0488561a5..f7ccb9bd507 100644 --- a/common/src/main/java/org/tron/core/Constant.java +++ b/common/src/main/java/org/tron/core/Constant.java @@ -421,9 +421,6 @@ public class Constant { public static final String COMMITTEE_ALLOW_TVM_BLOB = "committee.allowTvmBlob"; - public static final String COMMITTEE_ALLOW_TVM_SELFDESTRUCT_RESTRICTION = - "committee.allowTvmSelfdestructRestriction"; - public static final String COMMITTEE_PROPOSAL_EXPIRE_TIME = "committee.proposalExpireTime"; } diff --git a/framework/src/main/java/org/tron/core/config/args/Args.java b/framework/src/main/java/org/tron/core/config/args/Args.java index 70de1b5d443..7d95f28148e 100644 --- a/framework/src/main/java/org/tron/core/config/args/Args.java +++ b/framework/src/main/java/org/tron/core/config/args/Args.java @@ -250,7 +250,6 @@ public static void clearParam() { PARAMETER.consensusLogicOptimization = 0; PARAMETER.allowTvmCancun = 0; PARAMETER.allowTvmBlob = 0; - PARAMETER.allowTvmSelfdestructRestriction = 0; PARAMETER.rpcMaxRstStream = 0; PARAMETER.rpcSecondsPerWindow = 0; } @@ -1272,10 +1271,6 @@ public static void setParam(final Config config) { config.hasPath(Constant.COMMITTEE_ALLOW_TVM_BLOB) ? config .getInt(Constant.COMMITTEE_ALLOW_TVM_BLOB) : 0; - PARAMETER.allowTvmSelfdestructRestriction = - config.hasPath(Constant.COMMITTEE_ALLOW_TVM_SELFDESTRUCT_RESTRICTION) ? config - .getInt(Constant.COMMITTEE_ALLOW_TVM_SELFDESTRUCT_RESTRICTION) : 0; - logConfig(); } diff --git a/framework/src/test/java/org/tron/common/ParameterTest.java b/framework/src/test/java/org/tron/common/ParameterTest.java index 6724ced20b5..2f65189ac1c 100644 --- a/framework/src/test/java/org/tron/common/ParameterTest.java +++ b/framework/src/test/java/org/tron/common/ParameterTest.java @@ -322,7 +322,5 @@ public void testCommonParameter() { assertEquals(1, parameter.getAllowEnergyAdjustment()); parameter.setMaxCreateAccountTxSize(1000); assertEquals(1000, parameter.getMaxCreateAccountTxSize()); - parameter.setAllowTvmSelfdestructRestriction(1); - assertEquals(1, parameter.getAllowTvmSelfdestructRestriction()); } } From e0d167f6847d429b801265232e4e84ac2c804f44 Mon Sep 17 00:00:00 2001 From: zeusoo001 <161573193+zeusoo001@users.noreply.github.com> Date: Mon, 15 Sep 2025 17:29:32 +0800 Subject: [PATCH 49/59] fet(event): optimize event service to obtain transaction information (#6443) * feat(event): solve the problem of concurrent access to a single SR * feat(event): solve the concurrency problem of single SR obtaining solid ID --- .../src/main/java/org/tron/core/Wallet.java | 32 +------------- .../main/java/org/tron/core/db/Manager.java | 35 +++++++++++++++ .../core/services/event/BlockEventGet.java | 17 ++++---- .../services/event/HistoryEventService.java | 8 +++- .../java/org/tron/core/db/ManagerTest.java | 43 +++++++++++++++++++ 5 files changed, 94 insertions(+), 41 deletions(-) diff --git a/framework/src/main/java/org/tron/core/Wallet.java b/framework/src/main/java/org/tron/core/Wallet.java index 5397648d106..13ca5e31afa 100755 --- a/framework/src/main/java/org/tron/core/Wallet.java +++ b/framework/src/main/java/org/tron/core/Wallet.java @@ -2771,37 +2771,7 @@ public BytesMessage getShieldTransactionHash(Transaction transaction) } public TransactionInfoList getTransactionInfoByBlockNum(long blockNum) { - TransactionInfoList.Builder transactionInfoList = TransactionInfoList.newBuilder(); - - try { - TransactionRetCapsule result = dbManager.getTransactionRetStore() - .getTransactionInfoByBlockNum(ByteArray.fromLong(blockNum)); - - if (!Objects.isNull(result) && !Objects.isNull(result.getInstance())) { - result.getInstance().getTransactioninfoList().forEach( - transactionInfo -> transactionInfoList.addTransactionInfo(transactionInfo) - ); - } else { - Block block = chainBaseManager.getBlockByNum(blockNum).getInstance(); - - if (block != null) { - List listTransaction = block.getTransactionsList(); - for (Transaction transaction : listTransaction) { - TransactionInfoCapsule transactionInfoCapsule = dbManager.getTransactionHistoryStore() - .get(Sha256Hash.hash(CommonParameter.getInstance() - .isECKeyCryptoEngine(), transaction.getRawData().toByteArray())); - - if (transactionInfoCapsule != null) { - transactionInfoList.addTransactionInfo(transactionInfoCapsule.getInstance()); - } - } - } - } - } catch (BadItemException | ItemNotFoundException e) { - logger.warn(e.getMessage()); - } - - return transactionInfoList.build(); + return dbManager.getTransactionInfoByBlockNum(blockNum); } public NodeList listNodes() { diff --git a/framework/src/main/java/org/tron/core/db/Manager.java b/framework/src/main/java/org/tron/core/db/Manager.java index 17e2687e98b..ef14619bf02 100644 --- a/framework/src/main/java/org/tron/core/db/Manager.java +++ b/framework/src/main/java/org/tron/core/db/Manager.java @@ -163,6 +163,7 @@ import org.tron.core.store.WitnessScheduleStore; import org.tron.core.store.WitnessStore; import org.tron.core.utils.TransactionRegister; +import org.tron.protos.Protocol; import org.tron.protos.Protocol.AccountType; import org.tron.protos.Protocol.Permission; import org.tron.protos.Protocol.Transaction; @@ -2488,6 +2489,40 @@ private boolean isBlockWaitingLock() { return blockWaitLock.get() > NO_BLOCK_WAITING_LOCK; } + public TransactionInfoList getTransactionInfoByBlockNum(long blockNum) { + TransactionInfoList.Builder transactionInfoList = TransactionInfoList.newBuilder(); + + try { + TransactionRetCapsule result = getTransactionRetStore() + .getTransactionInfoByBlockNum(ByteArray.fromLong(blockNum)); + + if (!Objects.isNull(result) && !Objects.isNull(result.getInstance())) { + result.getInstance().getTransactioninfoList().forEach( + transactionInfo -> transactionInfoList.addTransactionInfo(transactionInfo) + ); + } else { + Protocol.Block block = chainBaseManager.getBlockByNum(blockNum).getInstance(); + + if (block != null) { + List listTransaction = block.getTransactionsList(); + for (Transaction transaction : listTransaction) { + TransactionInfoCapsule transactionInfoCapsule = getTransactionHistoryStore() + .get(Sha256Hash.hash(CommonParameter.getInstance() + .isECKeyCryptoEngine(), transaction.getRawData().toByteArray())); + + if (transactionInfoCapsule != null) { + transactionInfoList.addTransactionInfo(transactionInfoCapsule.getInstance()); + } + } + } + } + } catch (BadItemException | ItemNotFoundException e) { + logger.warn(e.getMessage()); + } + + return transactionInfoList.build(); + } + public void close() { stopRePushThread(); stopRePushTriggerThread(); diff --git a/framework/src/main/java/org/tron/core/services/event/BlockEventGet.java b/framework/src/main/java/org/tron/core/services/event/BlockEventGet.java index bf668a3e0b6..28f66de5ac9 100644 --- a/framework/src/main/java/org/tron/core/services/event/BlockEventGet.java +++ b/framework/src/main/java/org/tron/core/services/event/BlockEventGet.java @@ -57,10 +57,16 @@ public BlockEvent getBlockEvent(long blockNum) throws Exception { BlockCapsule block = manager.getChainBaseManager().getBlockByNum(blockNum); block.getTransactions().forEach(t -> t.setBlockNum(block.getNum())); long solidNum = manager.getDynamicPropertiesStore().getLatestSolidifiedBlockNum(); + long headNum = manager.getHeadBlockNum(); + // solve the single SR concurrency problem + if (solidNum >= headNum && headNum > 0) { + solidNum = headNum - 1; + } BlockEvent blockEvent = new BlockEvent(); blockEvent.setBlockId(block.getBlockId()); blockEvent.setParentId(block.getParentBlockId()); blockEvent.setSolidId(manager.getChainBaseManager().getBlockIdByNum(solidNum)); + if (instance.isBlockLogTriggerEnable()) { blockEvent.setBlockLogTriggerCapsule(getBlockLogTrigger(block, solidNum)); } @@ -88,18 +94,13 @@ public BlockEvent getBlockEvent(long blockNum) throws Exception { } public SmartContractTrigger getContractTrigger(BlockCapsule block, long solidNum) { - TransactionRetCapsule result; - try { - result = manager.getChainBaseManager().getTransactionRetStore() - .getTransactionInfoByBlockNum(ByteArray.fromLong(block.getNum())); - } catch (BadItemException e) { - throw new RuntimeException(e); - } + + GrpcAPI.TransactionInfoList list = manager.getTransactionInfoByBlockNum(block.getNum()); SmartContractTrigger contractTrigger = new SmartContractTrigger(); for (int i = 0; i < block.getTransactions().size(); i++) { Protocol.Transaction tx = block.getInstance().getTransactions(i); - Protocol.TransactionInfo txInfo = result.getInstance().getTransactioninfo(i); + Protocol.TransactionInfo txInfo = list.getTransactionInfo(i); List triggers = parseLogs(tx, txInfo); for (ContractTrigger trigger : triggers) { diff --git a/framework/src/main/java/org/tron/core/services/event/HistoryEventService.java b/framework/src/main/java/org/tron/core/services/event/HistoryEventService.java index 8f79ee47a3c..56d365972f6 100644 --- a/framework/src/main/java/org/tron/core/services/event/HistoryEventService.java +++ b/framework/src/main/java/org/tron/core/services/event/HistoryEventService.java @@ -54,7 +54,10 @@ private void syncEvent() { try { long tmp = instance.getStartSyncBlockNum(); long endNum = manager.getDynamicPropertiesStore().getLatestSolidifiedBlockNum(); - while (tmp <= endNum) { + while (tmp < endNum) { + if (thread.isInterrupted()) { + throw new InterruptedException(); + } if (instance.isUseNativeQueue()) { Thread.sleep(20); } else if (instance.isBusy()) { @@ -67,7 +70,8 @@ private void syncEvent() { tmp++; endNum = manager.getDynamicPropertiesStore().getLatestSolidifiedBlockNum(); } - initEventService(manager.getChainBaseManager().getBlockIdByNum(endNum)); + long startNum = endNum == 0 ? 0 : endNum - 1; + initEventService(manager.getChainBaseManager().getBlockIdByNum(startNum)); } catch (InterruptedException e1) { logger.warn("History event service interrupted."); Thread.currentThread().interrupt(); diff --git a/framework/src/test/java/org/tron/core/db/ManagerTest.java b/framework/src/test/java/org/tron/core/db/ManagerTest.java index d05334c5b94..7b387398cf6 100755 --- a/framework/src/test/java/org/tron/core/db/ManagerTest.java +++ b/framework/src/test/java/org/tron/core/db/ManagerTest.java @@ -33,6 +33,7 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; +import org.tron.api.GrpcAPI; import org.tron.common.application.TronApplicationContext; import org.tron.common.crypto.ECKey; import org.tron.common.runtime.RuntimeImpl; @@ -52,7 +53,10 @@ import org.tron.core.capsule.AccountCapsule; import org.tron.core.capsule.AssetIssueCapsule; import org.tron.core.capsule.BlockCapsule; +import org.tron.core.capsule.BytesCapsule; import org.tron.core.capsule.TransactionCapsule; +import org.tron.core.capsule.TransactionInfoCapsule; +import org.tron.core.capsule.TransactionRetCapsule; import org.tron.core.capsule.WitnessCapsule; import org.tron.core.config.DefaultConfig; import org.tron.core.config.Parameter; @@ -1233,6 +1237,45 @@ public void testExpiration() { } + @Test + public void testGetTransactionInfoByBlockNum() throws Exception { + + Transaction transaction = Protocol.Transaction.newBuilder() + .addSignature(ByteString.copyFrom(new byte[1])).build(); + TransactionCapsule transactionCapsule = new TransactionCapsule(transaction); + + Protocol.BlockHeader.raw raw = Protocol.BlockHeader.raw.newBuilder().setNumber(1000L).build(); + Protocol.BlockHeader header = Protocol.BlockHeader.newBuilder().setRawData(raw).build(); + Block block = Block.newBuilder().setBlockHeader(header).addTransactions(transaction).build(); + + Protocol.TransactionInfo info = Protocol.TransactionInfo.newBuilder() + .setBlockNumber(1000L).build(); + + BlockCapsule blockCapsule = new BlockCapsule(block); + byte[] blockId = new BlockCapsule(block).getBlockId().getBytes(); + dbManager.getBlockIndexStore().put(ByteArray.fromLong(1000L), new BytesCapsule(blockId)); + dbManager.getBlockStore().put(blockId, blockCapsule); + dbManager.getTransactionHistoryStore().put(transactionCapsule.getTransactionId().getBytes(), + new TransactionInfoCapsule(info)); + + GrpcAPI.TransactionInfoList transactionInfoList = dbManager.getTransactionInfoByBlockNum(1000L); + + Assert.assertEquals(1, transactionInfoList.getTransactionInfoCount()); + + Protocol.TransactionRet ret = Protocol.TransactionRet.newBuilder() + .addTransactioninfo(info) + .addTransactioninfo(info).build(); + + TransactionRetCapsule transactionRetCapsule = new TransactionRetCapsule(ret.toByteArray()); + + dbManager.getTransactionRetStore() + .put(ByteArray.fromLong(1000L), transactionRetCapsule); + + transactionInfoList = dbManager.getTransactionInfoByBlockNum(1000L); + + Assert.assertEquals(2, transactionInfoList.getTransactionInfoCount()); + } + @Test public void blockTrigger() { Manager manager = spy(new Manager()); From 732e7bcc61d3e98aad6bb6be4574fdc84328018e Mon Sep 17 00:00:00 2001 From: Edward Date: Wed, 17 Sep 2025 16:58:32 +0800 Subject: [PATCH 50/59] fix(API): get now witness count (#6451) * add GetPaginatedNowWitnessList into SolidityNodeHttpApiService * add isSolidityRequest check for MaintenanceUnavailableException * add unit tests --- framework/src/main/java/org/tron/core/Wallet.java | 6 +++++- .../http/solidity/SolidityNodeHttpApiService.java | 5 +++++ .../src/test/java/org/tron/core/WalletTest.java | 15 +++++++++++++++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/framework/src/main/java/org/tron/core/Wallet.java b/framework/src/main/java/org/tron/core/Wallet.java index 13ca5e31afa..8c86f2f66ac 100755 --- a/framework/src/main/java/org/tron/core/Wallet.java +++ b/framework/src/main/java/org/tron/core/Wallet.java @@ -174,6 +174,7 @@ import org.tron.core.db.Manager; import org.tron.core.db.TransactionContext; import org.tron.core.db2.core.Chainbase; +import org.tron.core.db2.core.Chainbase.Cursor; import org.tron.core.exception.AccountResourceInsufficientException; import org.tron.core.exception.BadItemException; import org.tron.core.exception.ContractExeException; @@ -784,8 +785,11 @@ public WitnessList getPaginatedNowWitnessList(long offset, long limit) throws In the maintenance period, the VoteStores will be cleared. To avoid the race condition of VoteStores deleted but Witness vote counts not updated, return retry error. + Only apply to requests that rely on the latest block, + which means the normal fullnode requests with HEAD cursor. */ - if (chainBaseManager.getDynamicPropertiesStore().getStateFlag() == 1) { + boolean isMaintenance = chainBaseManager.getDynamicPropertiesStore().getStateFlag() == 1; + if (isMaintenance && !Args.getInstance().isSolidityNode() && getCursor() == Cursor.HEAD) { String message = "Service temporarily unavailable during maintenance period. Please try again later."; throw new MaintenanceUnavailableException(message); diff --git a/framework/src/main/java/org/tron/core/services/http/solidity/SolidityNodeHttpApiService.java b/framework/src/main/java/org/tron/core/services/http/solidity/SolidityNodeHttpApiService.java index ea08d2d42cf..359adfc2b39 100644 --- a/framework/src/main/java/org/tron/core/services/http/solidity/SolidityNodeHttpApiService.java +++ b/framework/src/main/java/org/tron/core/services/http/solidity/SolidityNodeHttpApiService.java @@ -44,6 +44,7 @@ import org.tron.core.services.http.GetNodeInfoServlet; import org.tron.core.services.http.GetNowBlockServlet; import org.tron.core.services.http.GetPaginatedAssetIssueListServlet; +import org.tron.core.services.http.GetPaginatedNowWitnessListServlet; import org.tron.core.services.http.GetRewardServlet; import org.tron.core.services.http.GetTransactionCountByBlockNumServlet; import org.tron.core.services.http.GetTransactionInfoByBlockNumServlet; @@ -92,6 +93,8 @@ public class SolidityNodeHttpApiService extends HttpService { @Autowired private ListWitnessesServlet listWitnessesServlet; @Autowired + private GetPaginatedNowWitnessListServlet getPaginatedNowWitnessListServlet; + @Autowired private GetAssetIssueListServlet getAssetIssueListServlet; @Autowired private GetPaginatedAssetIssueListServlet getPaginatedAssetIssueListServlet; @@ -174,6 +177,8 @@ protected void addServlet(ServletContextHandler context) { // same as FullNode context.addServlet(new ServletHolder(getAccountServlet), "/walletsolidity/getaccount"); context.addServlet(new ServletHolder(listWitnessesServlet), "/walletsolidity/listwitnesses"); + context.addServlet(new ServletHolder(getPaginatedNowWitnessListServlet), + "/walletsolidity/getpaginatednowwitnesslist"); context.addServlet(new ServletHolder(getAssetIssueListServlet), "/walletsolidity/getassetissuelist"); context.addServlet(new ServletHolder(getPaginatedAssetIssueListServlet), diff --git a/framework/src/test/java/org/tron/core/WalletTest.java b/framework/src/test/java/org/tron/core/WalletTest.java index 10b0aa178f6..e388d3375c4 100644 --- a/framework/src/test/java/org/tron/core/WalletTest.java +++ b/framework/src/test/java/org/tron/core/WalletTest.java @@ -71,6 +71,7 @@ import org.tron.core.capsule.VotesCapsule; import org.tron.core.capsule.WitnessCapsule; import org.tron.core.config.args.Args; +import org.tron.core.db2.core.Chainbase; import org.tron.core.exception.ContractExeException; import org.tron.core.exception.ContractValidateException; import org.tron.core.exception.MaintenanceUnavailableException; @@ -867,6 +868,20 @@ public void testGetPaginatedNowWitnessList_Error() { Assert.assertTrue("Should throw MaintenanceClearingException", e instanceof MaintenanceUnavailableException); } + + try { + Args.getInstance().setSolidityNode(true); + wallet.getPaginatedNowWitnessList(0, 10); + Args.getInstance().setSolidityNode(false); + + dbManager.setCursor(Chainbase.Cursor.SOLIDITY); + wallet.getPaginatedNowWitnessList(0, 10); + dbManager.setCursor(Chainbase.Cursor.HEAD); + } catch (Exception e) { + Assert.assertFalse("Should not throw MaintenanceClearingException", + e instanceof MaintenanceUnavailableException); + } + dbManager.getChainBaseManager().getDynamicPropertiesStore().saveStateFlag(0); } From 316de9aea9683237a60091ede11d58c2f2a03230 Mon Sep 17 00:00:00 2001 From: halibobo1205 <82020050+halibobo1205@users.noreply.github.com> Date: Thu, 18 Sep 2025 13:38:07 +0800 Subject: [PATCH 51/59] log(http): revert workaround for CodeQL java/error-message-exposure (#6449) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Revert change to HTTP error message handling — workaround broke clients depending on original error output. --- .../core/services/http/GetBrokerageServlet.java | 3 ++- .../tron/core/services/http/GetRewardServlet.java | 3 ++- .../GetTransactionByIdSolidityServlet.java | 14 ++++++++++++-- .../GetTransactionInfoByIdSolidityServlet.java | 15 +++++++++++++-- 4 files changed, 29 insertions(+), 6 deletions(-) diff --git a/framework/src/main/java/org/tron/core/services/http/GetBrokerageServlet.java b/framework/src/main/java/org/tron/core/services/http/GetBrokerageServlet.java index 6ef22e198f9..1fbd94fe690 100644 --- a/framework/src/main/java/org/tron/core/services/http/GetBrokerageServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/GetBrokerageServlet.java @@ -28,7 +28,8 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) { response.getWriter().println("{\"brokerage\": " + value + "}"); } catch (DecoderException | IllegalArgumentException e) { try { - response.getWriter().println("{\"Error\": " + "\"INVALID address\"}"); + response.getWriter() + .println("{\"Error\": " + "\"INVALID address, " + e.getMessage() + "\"}"); } catch (IOException ioe) { logger.debug("IOException: {}", ioe.getMessage()); } diff --git a/framework/src/main/java/org/tron/core/services/http/GetRewardServlet.java b/framework/src/main/java/org/tron/core/services/http/GetRewardServlet.java index 78042072df8..c4d97f46c57 100644 --- a/framework/src/main/java/org/tron/core/services/http/GetRewardServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/GetRewardServlet.java @@ -27,7 +27,8 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) { response.getWriter().println("{\"reward\": " + value + "}"); } catch (DecoderException | IllegalArgumentException e) { try { - response.getWriter().println("{\"Error\": " + "\"INVALID address\"}"); + response.getWriter() + .println("{\"Error\": " + "\"INVALID address, " + e.getMessage() + "\"}"); } catch (IOException ioe) { logger.debug("IOException: {}", ioe.getMessage()); } diff --git a/framework/src/main/java/org/tron/core/services/http/solidity/GetTransactionByIdSolidityServlet.java b/framework/src/main/java/org/tron/core/services/http/solidity/GetTransactionByIdSolidityServlet.java index 5998bc0850f..f98c7450afc 100644 --- a/framework/src/main/java/org/tron/core/services/http/solidity/GetTransactionByIdSolidityServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/solidity/GetTransactionByIdSolidityServlet.java @@ -30,7 +30,12 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) { String input = request.getParameter("value"); fillResponse(ByteString.copyFrom(ByteArray.fromHexString(input)), visible, response); } catch (Exception e) { - Util.processError(e, response); + logger.debug("Exception: {}", e.getMessage()); + try { + response.getWriter().println(e.getMessage()); + } catch (IOException ioe) { + logger.debug("IOException: {}", ioe.getMessage()); + } } } @@ -41,7 +46,12 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response) JsonFormat.merge(params.getParams(), build, params.isVisible()); fillResponse(build.build().getValue(), params.isVisible(), response); } catch (Exception e) { - Util.processError(e, response); + logger.debug("Exception: {}", e.getMessage()); + try { + response.getWriter().println(e.getMessage()); + } catch (IOException ioe) { + logger.debug("IOException: {}", ioe.getMessage()); + } } } diff --git a/framework/src/main/java/org/tron/core/services/http/solidity/GetTransactionInfoByIdSolidityServlet.java b/framework/src/main/java/org/tron/core/services/http/solidity/GetTransactionInfoByIdSolidityServlet.java index 197f5aaec0d..0408215f09d 100644 --- a/framework/src/main/java/org/tron/core/services/http/solidity/GetTransactionInfoByIdSolidityServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/solidity/GetTransactionInfoByIdSolidityServlet.java @@ -1,6 +1,7 @@ package org.tron.core.services.http.solidity; import com.google.protobuf.ByteString; +import java.io.IOException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; @@ -36,7 +37,12 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) { response.getWriter().println(JsonFormat.printToString(transInfo, visible)); } } catch (Exception e) { - Util.processError(e, response); + logger.debug("Exception: {}", e.getMessage()); + try { + response.getWriter().println(e.getMessage()); + } catch (IOException ioe) { + logger.debug("IOException: {}", ioe.getMessage()); + } } } @@ -54,7 +60,12 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response) response.getWriter().println(JsonFormat.printToString(transInfo, params.isVisible())); } } catch (Exception e) { - Util.processError(e, response); + logger.debug("Exception: {}", e.getMessage()); + try { + response.getWriter().println(e.getMessage()); + } catch (IOException ioe) { + logger.debug("IOException: {}", ioe.getMessage()); + } } } From ac6bc5fc2af3dc41479eda62701d9094964275d7 Mon Sep 17 00:00:00 2001 From: halibobo1205 <82020050+halibobo1205@users.noreply.github.com> Date: Thu, 18 Sep 2025 13:47:01 +0800 Subject: [PATCH 52/59] fix(command): add missing help message for '--keystore-factory' (#6450) --- framework/src/main/java/org/tron/core/config/args/Args.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/src/main/java/org/tron/core/config/args/Args.java b/framework/src/main/java/org/tron/core/config/args/Args.java index 7d95f28148e..a716997419e 100644 --- a/framework/src/main/java/org/tron/core/config/args/Args.java +++ b/framework/src/main/java/org/tron/core/config/args/Args.java @@ -347,7 +347,7 @@ private static String getCommitIdAbbrev() { private static Map getOptionGroup() { String[] tronOption = new String[] {"version", "help", "shellConfFileName", "logbackPath", - "eventSubscribe", "solidityNode", "keystore"}; + "eventSubscribe", "solidityNode", "keystoreFactory"}; String[] dbOption = new String[] {"outputDirectory"}; String[] witnessOption = new String[] {"witness", "privateKey"}; String[] vmOption = new String[] {"debug"}; From 7f237fd5ec0bfc89d7fa47c4ce80c5f020693cb6 Mon Sep 17 00:00:00 2001 From: halibobo1205 <82020050+halibobo1205@users.noreply.github.com> Date: Wed, 24 Sep 2025 11:54:45 +0800 Subject: [PATCH 53/59] feat(exit): throw TronError if the JDK does not support (#6455) --- .../org/tron/core/exception/TronError.java | 1 + .../java/org/tron/core/config/args/Args.java | 14 ++++++++- .../tron/core/exception/TronErrorTest.java | 31 +++++++++++++++++-- .../common/org/tron/common/arch/Arch.java | 4 +-- 4 files changed, 45 insertions(+), 5 deletions(-) diff --git a/common/src/main/java/org/tron/core/exception/TronError.java b/common/src/main/java/org/tron/core/exception/TronError.java index 9d11d249476..2ea07499e55 100644 --- a/common/src/main/java/org/tron/core/exception/TronError.java +++ b/common/src/main/java/org/tron/core/exception/TronError.java @@ -47,6 +47,7 @@ public enum ErrCode { LOG_LOAD(1), WITNESS_INIT(1), RATE_LIMITER_INIT(1), + JDK_VERSION(1), SOLID_NODE_INIT(0); private final int code; diff --git a/framework/src/main/java/org/tron/core/config/args/Args.java b/framework/src/main/java/org/tron/core/config/args/Args.java index a716997419e..27739ff432e 100644 --- a/framework/src/main/java/org/tron/core/config/args/Args.java +++ b/framework/src/main/java/org/tron/core/config/args/Args.java @@ -1,6 +1,7 @@ package org.tron.core.config.args; import static java.lang.System.exit; +import static org.fusesource.jansi.Ansi.ansi; import static org.tron.common.math.Maths.max; import static org.tron.common.math.Maths.min; import static org.tron.core.Constant.ADD_PRE_FIX_BYTE_MAINNET; @@ -45,6 +46,7 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; +import org.fusesource.jansi.AnsiConsole; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.tron.common.arch.Arch; @@ -374,7 +376,17 @@ private static Map getOptionGroup() { * set parameters. */ public static void setParam(final String[] args, final String confFileName) { - Arch.throwIfUnsupportedJavaVersion(); + try { + Arch.throwIfUnsupportedJavaVersion(); + } catch (UnsupportedOperationException e) { + AnsiConsole.systemInstall(); + // To avoid confusion caused by silent execution when using -h or -v flags, + // errors are explicitly logged to the console in this context. + // Console output is not required for errors in other scenarios. + System.out.println(ansi().fgRed().a(e.getMessage()).reset()); + AnsiConsole.systemUninstall(); + throw new TronError(e, TronError.ErrCode.JDK_VERSION); + } clearParam(); // reset all parameters to avoid the influence in test JCommander.newBuilder().addObject(PARAMETER).build().parse(args); if (PARAMETER.version) { diff --git a/framework/src/test/java/org/tron/core/exception/TronErrorTest.java b/framework/src/test/java/org/tron/core/exception/TronErrorTest.java index 6976e73b013..5670a441bc6 100644 --- a/framework/src/test/java/org/tron/core/exception/TronErrorTest.java +++ b/framework/src/test/java/org/tron/core/exception/TronErrorTest.java @@ -24,6 +24,7 @@ import org.mockito.MockedStatic; import org.mockito.Mockito; import org.mockito.junit.MockitoJUnitRunner; +import org.tron.common.arch.Arch; import org.tron.common.log.LogService; import org.tron.common.parameter.RateLimiterInitialization; import org.tron.common.utils.ReflectUtils; @@ -41,7 +42,7 @@ public class TronErrorTest { public TemporaryFolder temporaryFolder = new TemporaryFolder(); @After - public void clearMocks() { + public void clearMocks() { Mockito.clearAllCaches(); Args.clearParam(); } @@ -115,7 +116,7 @@ public void rateLimiterServletInitTest() { @Test public void shutdownBlockTimeInitTest() { - Map params = new HashMap<>(); + Map params = new HashMap<>(); params.put(Constant.NODE_SHUTDOWN_BLOCK_TIME, "0"); params.put("storage.db.directory", "database"); Config config = ConfigFactory.defaultOverrides().withFallback( @@ -123,4 +124,30 @@ public void shutdownBlockTimeInitTest() { TronError thrown = assertThrows(TronError.class, () -> Args.setParam(config)); assertEquals(TronError.ErrCode.AUTO_STOP_PARAMS, thrown.getErrCode()); } + + @Test + public void testThrowIfUnsupportedJavaVersion() { + runArchTest(true, false, true); + runArchTest(true, true, false); + runArchTest(false, false, false); + } + + private void runArchTest(boolean isX86, boolean isJava8, boolean expectThrow) { + try (MockedStatic mocked = mockStatic(Arch.class)) { + mocked.when(Arch::isX86).thenReturn(isX86); + mocked.when(Arch::isJava8).thenReturn(isJava8); + mocked.when(Arch::getOsArch).thenReturn("x86_64"); + mocked.when(Arch::javaSpecificationVersion).thenReturn("17"); + mocked.when(Arch::withAll).thenReturn(""); + mocked.when(Arch::throwIfUnsupportedJavaVersion).thenCallRealMethod(); + + if (expectThrow) { + assertEquals(TronError.ErrCode.JDK_VERSION, assertThrows( + TronError.class, () -> Args.setParam(new String[]{}, Constant.TEST_CONF)).getErrCode()); + } else { + Arch.throwIfUnsupportedJavaVersion(); + } + } + } + } diff --git a/platform/src/main/java/common/org/tron/common/arch/Arch.java b/platform/src/main/java/common/org/tron/common/arch/Arch.java index e9edf8945d4..e91c626ff66 100644 --- a/platform/src/main/java/common/org/tron/common/arch/Arch.java +++ b/platform/src/main/java/common/org/tron/common/arch/Arch.java @@ -73,8 +73,8 @@ public static void throwIfUnsupportedJavaVersion() { if (isX86() && !isJava8()) { logger.info(withAll()); throw new UnsupportedOperationException(String.format( - "Java %s is required for %s architecture. Detected version %s", - "1.8 ", getOsArch(), javaSpecificationVersion())); + "Java %s is required for %s architecture. Detected version %s", "1.8", + getOsArch(), javaSpecificationVersion())); } } From 04f7991d01475f39432cb60be6886cd24f536be4 Mon Sep 17 00:00:00 2001 From: zeusoo001 <161573193+zeusoo001@users.noreply.github.com> Date: Thu, 25 Sep 2025 13:41:37 +0800 Subject: [PATCH 54/59] feat(db): optimize the compatibility of obtaining transaction info (#6453) * feat(db): optimize the compatibility of obtaining transaction information --- .../main/java/org/tron/core/db/Manager.java | 38 ++----------------- .../core/services/event/BlockEventGet.java | 36 +++--------------- .../services/event/HistoryEventService.java | 8 +++- .../services/jsonrpc/filters/LogMatch.java | 14 +++---- .../java/org/tron/core/db/ManagerTest.java | 2 + .../tron/core/event/BlockEventGetTest.java | 23 ++++++----- .../core/event/HistoryEventServiceTest.java | 2 +- 7 files changed, 35 insertions(+), 88 deletions(-) diff --git a/framework/src/main/java/org/tron/core/db/Manager.java b/framework/src/main/java/org/tron/core/db/Manager.java index ef14619bf02..586c3d83857 100644 --- a/framework/src/main/java/org/tron/core/db/Manager.java +++ b/framework/src/main/java/org/tron/core/db/Manager.java @@ -48,6 +48,7 @@ import org.bouncycastle.util.encoders.Hex; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import org.tron.api.GrpcAPI; import org.tron.api.GrpcAPI.TransactionInfoList; import org.tron.common.args.GenesisBlock; import org.tron.common.bloom.Bloom; @@ -2158,25 +2159,7 @@ private void processTransactionTrigger(BlockCapsule newBlock) { // need to set eth compatible data from transactionInfoList if (EventPluginLoader.getInstance().isTransactionLogTriggerEthCompatible() && newBlock.getNum() != 0) { - TransactionInfoList transactionInfoList = TransactionInfoList.newBuilder().build(); - TransactionInfoList.Builder transactionInfoListBuilder = TransactionInfoList.newBuilder(); - - try { - TransactionRetCapsule result = chainBaseManager.getTransactionRetStore() - .getTransactionInfoByBlockNum(ByteArray.fromLong(newBlock.getNum())); - - if (!Objects.isNull(result) && !Objects.isNull(result.getInstance())) { - result.getInstance().getTransactioninfoList().forEach( - transactionInfoListBuilder::addTransactionInfo - ); - - transactionInfoList = transactionInfoListBuilder.build(); - } - } catch (BadItemException e) { - logger.error("PostBlockTrigger getTransactionInfoList blockNum = {}, error is {}.", - newBlock.getNum(), e.getMessage()); - } - + TransactionInfoList transactionInfoList = getTransactionInfoByBlockNum(newBlock.getNum()); if (transactionCapsuleList.size() == transactionInfoList.getTransactionInfoCount()) { long cumulativeEnergyUsed = 0; long cumulativeLogCount = 0; @@ -2234,21 +2217,8 @@ private void postLogsFilter(final BlockCapsule blockCapsule, boolean solidified, boolean removed) { if (!blockCapsule.getTransactions().isEmpty()) { long blockNumber = blockCapsule.getNum(); - List transactionInfoList = new ArrayList<>(); - - try { - TransactionRetCapsule result = chainBaseManager.getTransactionRetStore() - .getTransactionInfoByBlockNum(ByteArray.fromLong(blockNumber)); - - if (!Objects.isNull(result) && !Objects.isNull(result.getInstance())) { - transactionInfoList.addAll(result.getInstance().getTransactioninfoList()); - } - } catch (BadItemException e) { - logger.error("ProcessLogsFilter getTransactionInfoList blockNum = {}, error is {}.", - blockNumber, e.getMessage()); - return; - } - + List transactionInfoList + = getTransactionInfoByBlockNum(blockNumber).getTransactionInfoList(); LogsFilterCapsule logsFilterCapsule = new LogsFilterCapsule(blockNumber, blockCapsule.getBlockId().toString(), blockCapsule.getBloom(), transactionInfoList, solidified, removed); diff --git a/framework/src/main/java/org/tron/core/services/event/BlockEventGet.java b/framework/src/main/java/org/tron/core/services/event/BlockEventGet.java index 28f66de5ac9..122a61222c3 100644 --- a/framework/src/main/java/org/tron/core/services/event/BlockEventGet.java +++ b/framework/src/main/java/org/tron/core/services/event/BlockEventGet.java @@ -329,22 +329,10 @@ public List getTransactionLogTrigger(BlockCapsule if (!EventPluginLoader.getInstance().isTransactionLogTriggerEthCompatible()) { return getTransactionTriggers(block, solidNum); } + + GrpcAPI.TransactionInfoList transactionInfoList + = manager.getTransactionInfoByBlockNum(block.getNum()); List transactionCapsuleList = block.getTransactions(); - GrpcAPI.TransactionInfoList transactionInfoList = GrpcAPI - .TransactionInfoList.newBuilder().build(); - GrpcAPI.TransactionInfoList.Builder transactionInfoListBuilder = GrpcAPI - .TransactionInfoList.newBuilder(); - try { - TransactionRetCapsule result = manager.getChainBaseManager().getTransactionRetStore() - .getTransactionInfoByBlockNum(ByteArray.fromLong(block.getNum())); - if (!Objects.isNull(result) && !Objects.isNull(result.getInstance())) { - result.getInstance().getTransactioninfoList() - .forEach(transactionInfoListBuilder::addTransactionInfo); - transactionInfoList = transactionInfoListBuilder.build(); - } - } catch (BadItemException e) { - logger.error("Get TransactionInfo failed, blockNum {}, {}.", block.getNum(), e.getMessage()); - } if (transactionCapsuleList.size() != transactionInfoList.getTransactionInfoCount()) { logger.error("Get TransactionInfo size not eq, blockNum {}, {}, {}", block.getNum(), transactionCapsuleList.size(), @@ -385,22 +373,8 @@ public List getTransactionTriggers(BlockCapsule bl return list; } - GrpcAPI.TransactionInfoList transactionInfoList = GrpcAPI - .TransactionInfoList.newBuilder().build(); - GrpcAPI.TransactionInfoList.Builder transactionInfoListBuilder = GrpcAPI - .TransactionInfoList.newBuilder(); - try { - TransactionRetCapsule result = manager.getChainBaseManager().getTransactionRetStore() - .getTransactionInfoByBlockNum(ByteArray.fromLong(block.getNum())); - if (!Objects.isNull(result) && !Objects.isNull(result.getInstance())) { - result.getInstance().getTransactioninfoList() - .forEach(transactionInfoListBuilder::addTransactionInfo); - transactionInfoList = transactionInfoListBuilder.build(); - } - } catch (Exception e) { - logger.warn("Get TransactionInfo failed, blockNum {}, {}.", block.getNum(), e.getMessage()); - } - + GrpcAPI.TransactionInfoList transactionInfoList + = manager.getTransactionInfoByBlockNum(block.getNum()); if (block.getTransactions().size() != transactionInfoList.getTransactionInfoCount()) { for (TransactionCapsule t : block.getTransactions()) { TransactionLogTriggerCapsule triggerCapsule = new TransactionLogTriggerCapsule(t, block); diff --git a/framework/src/main/java/org/tron/core/services/event/HistoryEventService.java b/framework/src/main/java/org/tron/core/services/event/HistoryEventService.java index 56d365972f6..d618e05e0cd 100644 --- a/framework/src/main/java/org/tron/core/services/event/HistoryEventService.java +++ b/framework/src/main/java/org/tron/core/services/event/HistoryEventService.java @@ -45,7 +45,13 @@ public void init() { public void close() { if (thread != null) { - thread.interrupt(); + try { + thread.interrupt(); + thread.join(1000); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + logger.warn("Wait close timeout, {}", e.getMessage()); + } } logger.info("History event service close."); } diff --git a/framework/src/main/java/org/tron/core/services/jsonrpc/filters/LogMatch.java b/framework/src/main/java/org/tron/core/services/jsonrpc/filters/LogMatch.java index 2c3d3c2a52e..cf958d1e2cb 100644 --- a/framework/src/main/java/org/tron/core/services/jsonrpc/filters/LogMatch.java +++ b/framework/src/main/java/org/tron/core/services/jsonrpc/filters/LogMatch.java @@ -83,17 +83,13 @@ public LogFilterElement[] matchBlockOneByOne() List logFilterElementList = new ArrayList<>(); for (long blockNum : blockNumList) { - TransactionRetCapsule transactionRetCapsule = - manager.getTransactionRetStore() - .getTransactionInfoByBlockNum(ByteArray.fromLong(blockNum)); - if (transactionRetCapsule == null) { - //if query condition (address and topics) is empty, we will traversal every block, - //include empty block + List transactionInfoList = + manager.getTransactionInfoByBlockNum(blockNum).getTransactionInfoList(); + //if query condition (address and topics) is empty, we will traversal every block, + //include empty block + if (transactionInfoList.isEmpty()) { continue; } - TransactionRet transactionRet = transactionRetCapsule.getInstance(); - List transactionInfoList = transactionRet.getTransactioninfoList(); - String blockHash = manager.getChainBaseManager().getBlockIdByNum(blockNum).toString(); List matchedLog = matchBlock(logFilterWrapper.getLogFilter(), blockNum, blockHash, transactionInfoList, false); diff --git a/framework/src/test/java/org/tron/core/db/ManagerTest.java b/framework/src/test/java/org/tron/core/db/ManagerTest.java index 7b387398cf6..b61941c7112 100755 --- a/framework/src/test/java/org/tron/core/db/ManagerTest.java +++ b/framework/src/test/java/org/tron/core/db/ManagerTest.java @@ -1261,6 +1261,7 @@ public void testGetTransactionInfoByBlockNum() throws Exception { GrpcAPI.TransactionInfoList transactionInfoList = dbManager.getTransactionInfoByBlockNum(1000L); Assert.assertEquals(1, transactionInfoList.getTransactionInfoCount()); + Assert.assertEquals(1, transactionInfoList.getTransactionInfoList().size()); Protocol.TransactionRet ret = Protocol.TransactionRet.newBuilder() .addTransactioninfo(info) @@ -1274,6 +1275,7 @@ public void testGetTransactionInfoByBlockNum() throws Exception { transactionInfoList = dbManager.getTransactionInfoByBlockNum(1000L); Assert.assertEquals(2, transactionInfoList.getTransactionInfoCount()); + Assert.assertEquals(2, transactionInfoList.getTransactionInfoList().size()); } @Test diff --git a/framework/src/test/java/org/tron/core/event/BlockEventGetTest.java b/framework/src/test/java/org/tron/core/event/BlockEventGetTest.java index 25215ce4a32..9ee46118c14 100644 --- a/framework/src/test/java/org/tron/core/event/BlockEventGetTest.java +++ b/framework/src/test/java/org/tron/core/event/BlockEventGetTest.java @@ -19,6 +19,7 @@ import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.mockito.Mockito; +import org.tron.api.GrpcAPI; import org.tron.common.application.TronApplicationContext; import org.tron.common.logsfilter.EventPluginConfig; import org.tron.common.logsfilter.EventPluginLoader; @@ -233,6 +234,11 @@ public void getTransactionTriggers() throws Exception { bc = new BlockCapsule(100, ByteString.empty(), 1, transactionList); + Manager manager = mock(Manager.class); + ReflectUtils.setFieldValue(blockEventGet, "manager", manager); + Mockito.when(manager.getTransactionInfoByBlockNum(1)) + .thenReturn(GrpcAPI.TransactionInfoList.newBuilder().build()); + list = blockEventGet.getTransactionTriggers(bc, 1); Assert.assertEquals(1, list.size()); Assert.assertEquals(100, list.get(0).getTransactionLogTrigger().getTimeStamp()); @@ -253,21 +259,14 @@ public void getTransactionTriggers() throws Exception { .addContractResult(ByteString.copyFrom(ByteArray.fromHexString("112233"))) .setReceipt(resourceBuild.build()); - Manager manager = mock(Manager.class); - ReflectUtils.setFieldValue(blockEventGet, "manager", manager); - ChainBaseManager chainBaseManager = mock(ChainBaseManager.class); Mockito.when(manager.getChainBaseManager()).thenReturn(chainBaseManager); - TransactionRetStore transactionRetStore = mock(TransactionRetStore.class); - Mockito.when(chainBaseManager.getTransactionRetStore()).thenReturn(transactionRetStore); - - Protocol.TransactionRet transactionRet = Protocol.TransactionRet.newBuilder() - .addTransactioninfo(infoBuild.build()).build(); - TransactionRetCapsule result = new TransactionRetCapsule(transactionRet.toByteArray()); + GrpcAPI.TransactionInfoList result = GrpcAPI.TransactionInfoList.newBuilder() + .addTransactionInfo(infoBuild.build()).build(); - Mockito.when(transactionRetStore.getTransactionInfoByBlockNum(ByteArray.fromLong(0))) + Mockito.when(manager.getTransactionInfoByBlockNum(0)) .thenReturn(result); Protocol.Block block = Protocol.Block.newBuilder() @@ -286,8 +285,8 @@ public void getTransactionTriggers() throws Exception { Assert.assertEquals(2, list.get(0).getTransactionLogTrigger().getEnergyUsageTotal()); - Mockito.when(transactionRetStore.getTransactionInfoByBlockNum(ByteArray.fromLong(0))) - .thenReturn(null); + Mockito.when(manager.getTransactionInfoByBlockNum(0)) + .thenReturn(GrpcAPI.TransactionInfoList.newBuilder().build()); list = blockEventGet.getTransactionTriggers(blockCapsule, 1); Assert.assertEquals(1, list.size()); Assert.assertEquals(1, diff --git a/framework/src/test/java/org/tron/core/event/HistoryEventServiceTest.java b/framework/src/test/java/org/tron/core/event/HistoryEventServiceTest.java index d56f1fc0993..1485d726235 100644 --- a/framework/src/test/java/org/tron/core/event/HistoryEventServiceTest.java +++ b/framework/src/test/java/org/tron/core/event/HistoryEventServiceTest.java @@ -23,7 +23,7 @@ public class HistoryEventServiceTest { HistoryEventService historyEventService = new HistoryEventService(); - @Test + @Test(timeout = 60_000) public void test() throws Exception { EventPluginLoader instance = mock(EventPluginLoader.class); Mockito.when(instance.isUseNativeQueue()).thenReturn(true); From b9753ce680583969448a1b8d3b137dc4fdbd9f32 Mon Sep 17 00:00:00 2001 From: halibobo1205 <82020050+halibobo1205@users.noreply.github.com> Date: Thu, 25 Sep 2025 17:22:33 +0800 Subject: [PATCH 55/59] feat(jvm): Optimize JVM GC logging for arm64 (#6457) --- README.md | 6 +++--- gradle/jdk17/java-tron.vmoptions | 2 +- start.sh.simple | 19 +++++++++---------- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 4a40bb7dff5..a5062af3a4b 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ Make sure you operate on `Linux` or `MacOS` operating systems, other operating s ### ARM64 64-bit version of `JDK 17` to be installed, other JDK versions are not supported yet. -### build +## Build Clone the repo and switch to the `master` branch ```bash @@ -141,7 +141,7 @@ $ nohup java -Xms9G -Xmx9G -XX:ReservedCodeCacheSize=256m \ ### ARM64 JDK 17 ```bash $ nohup java -Xms9G -Xmx9G -XX:+UseZGC \ - -Xlog:gc*:file=gc.log:time,uptime,level,tags:filecount=50,filesize=100M \ + -Xlog:gc,gc+heap:file=gc.log:time,tags,level:filecount=10,filesize=100M \ -XX:ReservedCodeCacheSize=256m \ -XX:+UseCodeCacheFlushing \ -XX:MetaspaceSize=256m \ @@ -181,7 +181,7 @@ $ nohup java -Xms9G -Xmx9G -XX:ReservedCodeCacheSize=256m \ ### ARM64 JDK 17 ```bash $ nohup java -Xms9G -Xmx9G -XX:+UseZGC \ - -Xlog:gc*:file=gc.log:time,uptime,level,tags:filecount=50,filesize=100M \ + -Xlog:gc,gc+heap:file=gc.log:time,tags,level:filecount=10,filesize=100M \ -XX:ReservedCodeCacheSize=256m \ -XX:+UseCodeCacheFlushing \ -XX:MetaspaceSize=256m \ diff --git a/gradle/jdk17/java-tron.vmoptions b/gradle/jdk17/java-tron.vmoptions index 91accd05016..7af3123d268 100644 --- a/gradle/jdk17/java-tron.vmoptions +++ b/gradle/jdk17/java-tron.vmoptions @@ -1,5 +1,5 @@ -XX:+UseZGC --Xlog:gc*:file=gc.log:time,uptime,level,tags:filecount=50,filesize=100M +-Xlog:gc,gc+heap:file=gc.log:time,tags,level:filecount=10,filesize=100M -XX:ReservedCodeCacheSize=256m -XX:+UseCodeCacheFlushing -XX:MetaspaceSize=256m diff --git a/start.sh.simple b/start.sh.simple index 72862d9bdfc..1cb2899da0d 100644 --- a/start.sh.simple +++ b/start.sh.simple @@ -31,12 +31,10 @@ # adjust JVM start -# Set the minimum and maximum heap size to 9G, adjust as needed +# Set the minimum heap size to 9G, adjust as needed VM_XMS="9G" # Set the maximum heap size to 9G, adjust as needed VM_XMX="9G" -# Set the maximum direct memory size to 1G, adjust as needed -VM_MAX_DIRECT_MEMORY_SIZE="1G" # adjust JVM end FULL_NODE_JAR="FullNode.jar" @@ -133,13 +131,14 @@ startService() { fi nohup "$JAVACMD" \ - -Xms"$VM_XMS" -Xmx"$VM_XMX" -XX:ReservedCodeCacheSize=256m \ - -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m \ - -XX:MaxDirectMemorySize="$VM_MAX_DIRECT_MEMORY_SIZE" \ - -Xloggc:gc.log -XX:+PrintGCDetails \ - -XX:+UseG1GC \ - -XX:MaxGCPauseMillis=40 \ - -XX:InitiatingHeapOccupancyPercent=45 \ + -Xms"$VM_XMS" -Xmx"$VM_XMX" \ + -XX:+UseZGC \ + -Xlog:gc,gc+heap:file=gc.log:time,tags,level:filecount=10,filesize=100M \ + -XX:ReservedCodeCacheSize=256m \ + -XX:+UseCodeCacheFlushing \ + -XX:MetaspaceSize=256m \ + -XX:MaxMetaspaceSize=512m \ + -XX:MaxDirectMemorySize=1g \ -XX:+HeapDumpOnOutOfMemoryError \ -jar "$FULL_NODE_JAR" "${FULL_START_OPT[@]}" \ >> start.log 2>&1 & From b5a7fcd7a4e47b45ca837519d73074dc2b4cf256 Mon Sep 17 00:00:00 2001 From: lxcmyf Date: Sun, 28 Sep 2025 15:19:29 +0800 Subject: [PATCH 56/59] fix(exception): wrap parameter check exceptions with TronError (#6454) --- .../main/java/org/tron/core/exception/TronError.java | 5 +++-- .../src/main/java/org/tron/core/config/args/Args.java | 10 +++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/common/src/main/java/org/tron/core/exception/TronError.java b/common/src/main/java/org/tron/core/exception/TronError.java index 2ea07499e55..f407c6dfe3c 100644 --- a/common/src/main/java/org/tron/core/exception/TronError.java +++ b/common/src/main/java/org/tron/core/exception/TronError.java @@ -47,8 +47,9 @@ public enum ErrCode { LOG_LOAD(1), WITNESS_INIT(1), RATE_LIMITER_INIT(1), - JDK_VERSION(1), - SOLID_NODE_INIT(0); + SOLID_NODE_INIT(0), + PARAMETER_INIT(1), + JDK_VERSION(1); private final int code; diff --git a/framework/src/main/java/org/tron/core/config/args/Args.java b/framework/src/main/java/org/tron/core/config/args/Args.java index 27739ff432e..df32ec9a00c 100644 --- a/framework/src/main/java/org/tron/core/config/args/Args.java +++ b/framework/src/main/java/org/tron/core/config/args/Args.java @@ -12,6 +12,7 @@ import static org.tron.core.Constant.MIN_PROPOSAL_EXPIRE_TIME; import static org.tron.core.config.Parameter.ChainConstant.BLOCK_PRODUCE_TIMEOUT_PERCENT; import static org.tron.core.config.Parameter.ChainConstant.MAX_ACTIVE_WITNESS_NUM; +import static org.tron.core.exception.TronError.ErrCode.PARAMETER_INIT; import com.beust.jcommander.JCommander; import com.beust.jcommander.ParameterDescription; @@ -1288,17 +1289,16 @@ public static void setParam(final Config config) { private static long getProposalExpirationTime(final Config config) { if (config.hasPath(Constant.COMMITTEE_PROPOSAL_EXPIRE_TIME)) { - throw new IllegalArgumentException("It is not allowed to configure " - + "commit.proposalExpireTime in config.conf, please set the value in " - + "block.proposalExpireTime."); + throw new TronError("It is not allowed to configure committee.proposalExpireTime in " + + "config.conf, please set the value in block.proposalExpireTime.", PARAMETER_INIT); } if (config.hasPath(Constant.BLOCK_PROPOSAL_EXPIRE_TIME)) { long proposalExpireTime = config.getLong(Constant.BLOCK_PROPOSAL_EXPIRE_TIME); if (proposalExpireTime <= MIN_PROPOSAL_EXPIRE_TIME || proposalExpireTime >= MAX_PROPOSAL_EXPIRE_TIME) { - throw new IllegalArgumentException("The value[block.proposalExpireTime] is only allowed to " + throw new TronError("The value[block.proposalExpireTime] is only allowed to " + "be greater than " + MIN_PROPOSAL_EXPIRE_TIME + " and less than " - + MAX_PROPOSAL_EXPIRE_TIME + "!"); + + MAX_PROPOSAL_EXPIRE_TIME + "!", PARAMETER_INIT); } return proposalExpireTime; } else { From 6e5eda8241c8560b1e0c435c5e588f9ef7764a83 Mon Sep 17 00:00:00 2001 From: halibobo1205 <82020050+halibobo1205@users.noreply.github.com> Date: Mon, 29 Sep 2025 15:30:36 +0800 Subject: [PATCH 57/59] test(grpc): mitigate GRPC test hangs with timeout rule (#6460) Add 30-seconds timeout rule as a workaround to prevent GRPC unit tests hanging at ThreadlessExecutor.waitAndDrain --- .../java/org/tron/core/services/RpcApiServicesTest.java | 5 +++++ .../src/test/java/org/tron/core/services/WalletApiTest.java | 5 +++++ .../services/filter/LiteFnQueryGrpcInterceptorTest.java | 4 ++++ .../core/services/filter/RpcApiAccessInterceptorTest.java | 5 +++++ .../src/test/java/org/tron/program/SolidityNodeTest.java | 6 ++++++ 5 files changed, 25 insertions(+) diff --git a/framework/src/test/java/org/tron/core/services/RpcApiServicesTest.java b/framework/src/test/java/org/tron/core/services/RpcApiServicesTest.java index f98e0b03a57..dbd06fabffe 100644 --- a/framework/src/test/java/org/tron/core/services/RpcApiServicesTest.java +++ b/framework/src/test/java/org/tron/core/services/RpcApiServicesTest.java @@ -11,13 +11,16 @@ import io.grpc.ManagedChannelBuilder; import java.io.IOException; import java.util.Objects; +import java.util.concurrent.TimeUnit; import org.junit.AfterClass; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.ClassRule; import org.junit.FixMethodOrder; +import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; +import org.junit.rules.Timeout; import org.junit.runners.MethodSorters; import org.tron.api.DatabaseGrpc; import org.tron.api.DatabaseGrpc.DatabaseBlockingStub; @@ -125,6 +128,8 @@ public class RpcApiServicesTest { private static WalletSolidityBlockingStub blockingStubPBFT = null; @ClassRule public static TemporaryFolder temporaryFolder = new TemporaryFolder(); + @Rule + public Timeout timeout = new Timeout(30, TimeUnit.SECONDS); private static ByteString ownerAddress; private static ByteString sk; private static ByteString ask; diff --git a/framework/src/test/java/org/tron/core/services/WalletApiTest.java b/framework/src/test/java/org/tron/core/services/WalletApiTest.java index 5b548b3032a..f9c95f018c4 100644 --- a/framework/src/test/java/org/tron/core/services/WalletApiTest.java +++ b/framework/src/test/java/org/tron/core/services/WalletApiTest.java @@ -8,8 +8,10 @@ import org.junit.Assert; import org.junit.BeforeClass; import org.junit.ClassRule; +import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; +import org.junit.rules.Timeout; import org.tron.api.GrpcAPI.EmptyMessage; import org.tron.api.WalletGrpc; import org.tron.common.application.Application; @@ -28,6 +30,9 @@ public class WalletApiTest { @ClassRule public static TemporaryFolder temporaryFolder = new TemporaryFolder(); + @Rule + public Timeout timeout = new Timeout(30, TimeUnit.SECONDS); + private static TronApplicationContext context; private static Application appT; diff --git a/framework/src/test/java/org/tron/core/services/filter/LiteFnQueryGrpcInterceptorTest.java b/framework/src/test/java/org/tron/core/services/filter/LiteFnQueryGrpcInterceptorTest.java index 6694ddf08df..b3cd5844b8d 100644 --- a/framework/src/test/java/org/tron/core/services/filter/LiteFnQueryGrpcInterceptorTest.java +++ b/framework/src/test/java/org/tron/core/services/filter/LiteFnQueryGrpcInterceptorTest.java @@ -14,6 +14,7 @@ import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.rules.TemporaryFolder; +import org.junit.rules.Timeout; import org.tron.api.GrpcAPI; import org.tron.api.WalletGrpc; import org.tron.api.WalletSolidityGrpc; @@ -47,6 +48,9 @@ public class LiteFnQueryGrpcInterceptorTest { @ClassRule public static final TemporaryFolder temporaryFolder = new TemporaryFolder(); + @Rule + public Timeout timeout = new Timeout(30, TimeUnit.SECONDS); + /** * init logic. */ diff --git a/framework/src/test/java/org/tron/core/services/filter/RpcApiAccessInterceptorTest.java b/framework/src/test/java/org/tron/core/services/filter/RpcApiAccessInterceptorTest.java index ced05c7c5b3..2e02125e014 100644 --- a/framework/src/test/java/org/tron/core/services/filter/RpcApiAccessInterceptorTest.java +++ b/framework/src/test/java/org/tron/core/services/filter/RpcApiAccessInterceptorTest.java @@ -19,8 +19,10 @@ import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.ClassRule; +import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; +import org.junit.rules.Timeout; import org.tron.api.GrpcAPI.BlockExtention; import org.tron.api.GrpcAPI.BlockReq; import org.tron.api.GrpcAPI.BytesMessage; @@ -53,6 +55,9 @@ public class RpcApiAccessInterceptorTest { @ClassRule public static final TemporaryFolder temporaryFolder = new TemporaryFolder(); + @Rule + public Timeout timeout = new Timeout(30, TimeUnit.SECONDS); + /** * init logic. */ diff --git a/framework/src/test/java/org/tron/program/SolidityNodeTest.java b/framework/src/test/java/org/tron/program/SolidityNodeTest.java index 80fb2a74607..943c73cb9ed 100755 --- a/framework/src/test/java/org/tron/program/SolidityNodeTest.java +++ b/framework/src/test/java/org/tron/program/SolidityNodeTest.java @@ -3,10 +3,13 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThrows; +import java.util.concurrent.TimeUnit; import javax.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.junit.Assert; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.Timeout; import org.tron.common.BaseTest; import org.tron.common.client.DatabaseGrpcClient; import org.tron.common.utils.PublicMethod; @@ -28,6 +31,9 @@ public class SolidityNodeTest extends BaseTest { static int rpcPort = PublicMethod.chooseRandomPort(); static int solidityHttpPort = PublicMethod.chooseRandomPort(); + @Rule + public Timeout timeout = new Timeout(30, TimeUnit.SECONDS); + static { Args.setParam(new String[] {"-d", dbPath(), "--solidity"}, Constant.TEST_CONF); Args.getInstance().setRpcPort(rpcPort); From a86947cc132b5773bdbd7f899a2e29bdab8835d3 Mon Sep 17 00:00:00 2001 From: halibobo1205 <82020050+halibobo1205@users.noreply.github.com> Date: Mon, 29 Sep 2025 15:33:03 +0800 Subject: [PATCH 58/59] feat(runtime): enforce Java 17 for ARM64 (#6459) --- .../tron/core/exception/TronErrorTest.java | 51 +++++++++++++++---- .../common/org/tron/common/arch/Arch.java | 8 ++- 2 files changed, 47 insertions(+), 12 deletions(-) diff --git a/framework/src/test/java/org/tron/core/exception/TronErrorTest.java b/framework/src/test/java/org/tron/core/exception/TronErrorTest.java index 5670a441bc6..53382ec265a 100644 --- a/framework/src/test/java/org/tron/core/exception/TronErrorTest.java +++ b/framework/src/test/java/org/tron/core/exception/TronErrorTest.java @@ -2,8 +2,11 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThrows; +import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; import com.typesafe.config.Config; import com.typesafe.config.ConfigFactory; @@ -127,25 +130,53 @@ public void shutdownBlockTimeInitTest() { @Test public void testThrowIfUnsupportedJavaVersion() { - runArchTest(true, false, true); - runArchTest(true, true, false); - runArchTest(false, false, false); + runArchTest("x86_64", "1.8", false); + runArchTest("x86_64", "11", true); + runArchTest("x86_64", "17", true); + runArchTest("aarch64", "17", false); + runArchTest("aarch64", "1.8", true); + runArchTest("aarch64", "11", true); } - private void runArchTest(boolean isX86, boolean isJava8, boolean expectThrow) { + private void runArchTest(String osArch, String javaVersion, boolean expectThrow) { try (MockedStatic mocked = mockStatic(Arch.class)) { + boolean isX86 = "x86_64".equals(osArch); + boolean isArm64 = "aarch64".equals(osArch); + + boolean isJava8 = "1.8".equals(javaVersion); + boolean isJava17 = "17".equals(javaVersion); + mocked.when(Arch::isX86).thenReturn(isX86); + mocked.when(Arch::isArm64).thenReturn(isArm64); + mocked.when(Arch::isJava8).thenReturn(isJava8); - mocked.when(Arch::getOsArch).thenReturn("x86_64"); - mocked.when(Arch::javaSpecificationVersion).thenReturn("17"); - mocked.when(Arch::withAll).thenReturn(""); + mocked.when(Arch::isJava17).thenReturn(isJava17); + + mocked.when(Arch::getOsArch).thenReturn(osArch); + mocked.when(Arch::javaSpecificationVersion).thenReturn(javaVersion); + mocked.when(Arch::withAll).thenReturn(String.format( + "Architecture: %s, Java Version: %s", osArch, javaVersion)); + mocked.when(Arch::throwIfUnsupportedJavaVersion).thenCallRealMethod(); if (expectThrow) { - assertEquals(TronError.ErrCode.JDK_VERSION, assertThrows( - TronError.class, () -> Args.setParam(new String[]{}, Constant.TEST_CONF)).getErrCode()); + TronError err = assertThrows( + TronError.class, () -> Args.setParam(new String[]{}, Constant.TEST_CONF)); + + String expectedJavaVersion = isX86 ? "1.8" : "17"; + String expectedMessage = String.format( + "Java %s is required for %s architecture. Detected version %s", + expectedJavaVersion, osArch, javaVersion); + assertEquals(expectedMessage, err.getCause().getMessage()); + assertEquals(TronError.ErrCode.JDK_VERSION, err.getErrCode()); + mocked.verify(Arch::withAll, times(1)); } else { - Arch.throwIfUnsupportedJavaVersion(); + try { + Arch.throwIfUnsupportedJavaVersion(); + } catch (Exception e) { + fail("Expected no exception, but got: " + e.getMessage()); + } + mocked.verify(Arch::withAll, never()); } } } diff --git a/platform/src/main/java/common/org/tron/common/arch/Arch.java b/platform/src/main/java/common/org/tron/common/arch/Arch.java index e91c626ff66..f115d1f07c2 100644 --- a/platform/src/main/java/common/org/tron/common/arch/Arch.java +++ b/platform/src/main/java/common/org/tron/common/arch/Arch.java @@ -69,11 +69,15 @@ public static boolean isJava8() { return javaSpecificationVersion().equals("1.8"); } + public static boolean isJava17() { + return javaSpecificationVersion().equals("17"); + } + public static void throwIfUnsupportedJavaVersion() { - if (isX86() && !isJava8()) { + if ((isX86() && !isJava8()) || (isArm64() && !isJava17())) { logger.info(withAll()); throw new UnsupportedOperationException(String.format( - "Java %s is required for %s architecture. Detected version %s", "1.8", + "Java %s is required for %s architecture. Detected version %s", isX86() ? "1.8" : "17", getOsArch(), javaSpecificationVersion())); } } From e19b2a1ecdaf6d9921e1151107a57e20152301ee Mon Sep 17 00:00:00 2001 From: Federico2014 Date: Mon, 29 Sep 2025 16:17:19 +0800 Subject: [PATCH 59/59] fix(config): replace the Exception with TronError (#6452) * replace the Exception with TronError * remove plaintext key --- .../org/tron/common/utils/LocalWitnesses.java | 12 +-- .../core/config/args/LocalWitnessTest.java | 88 +++++++------------ 2 files changed, 37 insertions(+), 63 deletions(-) diff --git a/chainbase/src/main/java/org/tron/common/utils/LocalWitnesses.java b/chainbase/src/main/java/org/tron/common/utils/LocalWitnesses.java index 812e4d7fa5b..7179045ea7e 100644 --- a/chainbase/src/main/java/org/tron/common/utils/LocalWitnesses.java +++ b/chainbase/src/main/java/org/tron/common/utils/LocalWitnesses.java @@ -25,6 +25,7 @@ import org.tron.common.crypto.SignInterface; import org.tron.common.crypto.SignUtils; import org.tron.core.config.Parameter.ChainConstant; +import org.tron.core.exception.TronError; @Slf4j(topic = "app") public class LocalWitnesses { @@ -78,13 +79,14 @@ private void validate(String privateKey) { if (StringUtils.isBlank(privateKey) || privateKey.length() != ChainConstant.PRIVATE_KEY_LENGTH) { - throw new IllegalArgumentException( - String.format("private key must be %d hex string, actual: %d", - ChainConstant.PRIVATE_KEY_LENGTH, - StringUtils.isBlank(privateKey) ? 0 : privateKey.length())); + throw new TronError(String.format("private key must be %d hex string, actual: %d", + ChainConstant.PRIVATE_KEY_LENGTH, + StringUtils.isBlank(privateKey) ? 0 : privateKey.length()), + TronError.ErrCode.WITNESS_INIT); } if (!StringUtil.isHexadecimal(privateKey)) { - throw new IllegalArgumentException("private key must be hex string"); + throw new TronError("private key must be hex string", + TronError.ErrCode.WITNESS_INIT); } } diff --git a/framework/src/test/java/org/tron/core/config/args/LocalWitnessTest.java b/framework/src/test/java/org/tron/core/config/args/LocalWitnessTest.java index 8b9da2c7bc3..15d64f3e074 100644 --- a/framework/src/test/java/org/tron/core/config/args/LocalWitnessTest.java +++ b/framework/src/test/java/org/tron/core/config/args/LocalWitnessTest.java @@ -15,19 +15,26 @@ package org.tron.core.config.args; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import com.google.common.collect.Lists; import java.io.IOException; +import java.security.SecureRandom; import org.junit.Assert; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; +import org.tron.common.utils.ByteArray; import org.tron.common.utils.LocalWitnesses; import org.tron.common.utils.PublicMethod; import org.tron.common.utils.StringUtil; import org.tron.core.Constant; +import org.tron.core.exception.TronError; +import org.tron.core.exception.TronError.ErrCode; public class LocalWitnessTest { @@ -42,7 +49,7 @@ public void setLocalWitness() { localWitness .setPrivateKeys( Lists.newArrayList( - PRIVATE_KEY)); + PRIVATE_KEY)); } @Test @@ -52,13 +59,13 @@ public void whenSetNullPrivateKey() { Assert.assertNotNull(localWitness.getPublicKey()); } - @Test(expected = IllegalArgumentException.class) + @Test(expected = TronError.class) public void whenSetEmptyPrivateKey() { localWitness.setPrivateKeys(Lists.newArrayList("")); fail("private key must be 64-bits hex string"); } - @Test(expected = IllegalArgumentException.class) + @Test(expected = TronError.class) public void whenSetBadFormatPrivateKey() { localWitness.setPrivateKeys(Lists.newArrayList("a111")); fail("private key must be 64-bits hex string"); @@ -104,62 +111,27 @@ public void testValidPrivateKeyWithPrefix() { @Test public void testInvalidPrivateKey() { LocalWitnesses localWitnesses = new LocalWitnesses(); + String expectedMessage = "private key must be 64 hex string"; + assertTronError(localWitnesses, null, expectedMessage); + assertTronError(localWitnesses, "", expectedMessage); + assertTronError(localWitnesses, " ", expectedMessage); + assertTronError(localWitnesses, "11111", expectedMessage); + String expectedMessage2 = "private key must be hex string"; + SecureRandom secureRandom = new SecureRandom(); + byte[] keyBytes = new byte[31]; + secureRandom.nextBytes(keyBytes); + final String privateKey = ByteArray.toHexString(keyBytes) + " "; + assertTronError(localWitnesses, privateKey, expectedMessage2); + final String privateKey2 = "xy" + ByteArray.toHexString(keyBytes); + assertTronError(localWitnesses, privateKey2, expectedMessage2); + } - try { - localWitnesses.addPrivateKeys(null); - fail("should throw IllegalArgumentException"); - } catch (IllegalArgumentException e) { - Assert.assertTrue(e.getMessage().contains("private key must be 64 hex string")); - } catch (Exception e) { - fail("should IllegalArgumentException,actual exception: " + e.getClass().getSimpleName()); - } - - try { - localWitnesses.addPrivateKeys(""); - fail("should throw IllegalArgumentException"); - } catch (IllegalArgumentException e) { - Assert.assertTrue(e.getMessage().contains("private key must be 64 hex string")); - } catch (Exception e) { - fail("should IllegalArgumentException,actual exception: " + e.getClass().getSimpleName()); - } - - try { - localWitnesses.addPrivateKeys(" "); - fail("should throw IllegalArgumentException"); - } catch (IllegalArgumentException e) { - Assert.assertTrue(e.getMessage().contains("private key must be 64 hex string")); - } catch (Exception e) { - fail("should IllegalArgumentException,actual exception: " + e.getClass().getSimpleName()); - } - - try { - localWitnesses.addPrivateKeys("11111"); - fail("should throw IllegalArgumentException"); - } catch (IllegalArgumentException e) { - Assert.assertTrue(e.getMessage().contains("private key must be 64 hex string")); - } catch (Exception e) { - fail("should IllegalArgumentException,actual exception: " + e.getClass().getSimpleName()); - } - - try { - String privateKey = "11111111111111111111111111111111111111111111111111111111111111 "; - localWitnesses.addPrivateKeys(privateKey); - fail("should throw IllegalArgumentException"); - } catch (IllegalArgumentException e) { - Assert.assertTrue(e.getMessage().contains("private key must be hex string")); - } catch (Exception e) { - fail("should IllegalArgumentException,actual exception: " + e.getClass().getSimpleName()); - } - - try { - String privateKey = "xy11111111111111111111111111111111111111111111111111111111111111"; - localWitnesses.addPrivateKeys(privateKey); - fail("should throw IllegalArgumentException"); - } catch (IllegalArgumentException e) { - Assert.assertTrue(e.getMessage().contains("private key must be hex string")); - } catch (Exception e) { - fail("should IllegalArgumentException,actual exception: " + e.getClass().getSimpleName()); - } + private void assertTronError(LocalWitnesses localWitnesses, String privateKey, + String expectedMessage) { + TronError thrown = assertThrows(TronError.class, + () -> localWitnesses.addPrivateKeys(privateKey)); + assertEquals(ErrCode.WITNESS_INIT, thrown.getErrCode()); + assertTrue(thrown.getMessage().contains(expectedMessage)); } @Test