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 c42276e01bb..b75eb682932 100644 --- a/common/src/main/java/org/tron/common/parameter/CommonParameter.java +++ b/common/src/main/java/org/tron/common/parameter/CommonParameter.java @@ -654,6 +654,14 @@ public class CommonParameter { @Setter public long allowCancelAllUnfreezeV2; + @Getter + @Setter + public boolean unsolidifiedBlockCheck; + + @Getter + @Setter + public int maxUnsolidifiedBlocks; + 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 f739358156f..634d08b79a9 100644 --- a/common/src/main/java/org/tron/core/Constant.java +++ b/common/src/main/java/org/tron/core/Constant.java @@ -371,4 +371,8 @@ public class Constant { public static final String DYNAMIC_CONFIG_CHECK_INTERVAL = "node.dynamicConfig.checkInterval"; public static final String COMMITTEE_ALLOW_TVM_SHANGHAI = "committee.allowTvmShangHai"; + + public static final String UNSOLIDIFIED_BLOCK_CHECK = "node.unsolidifiedBlockCheck"; + + public static final String MAX_UNSOLIDIFIED_BLOCKS = "node.maxUnsolidifiedBlocks"; } diff --git a/framework/src/main/java/org/tron/core/Wallet.java b/framework/src/main/java/org/tron/core/Wallet.java index 0865e202974..b14fcdb1167 100755 --- a/framework/src/main/java/org/tron/core/Wallet.java +++ b/framework/src/main/java/org/tron/core/Wallet.java @@ -498,6 +498,14 @@ public GrpcAPI.Return broadcastTransaction(Transaction signedTransaction) { Sha256Hash txID = trx.getTransactionId(); try { TransactionMessage message = new TransactionMessage(signedTransaction.toByteArray()); + + if (tronNetDelegate.isBlockUnsolidified()) { + logger.warn("Broadcast transaction {} has failed, block unsolidified.", txID); + return builder.setResult(false).setCode(response_code.BLOCK_UNSOLIDIFIED) + .setMessage(ByteString.copyFromUtf8("Block unsolidified.")) + .build(); + } + if (minEffectiveConnection != 0) { if (tronNetDelegate.getActivePeer().isEmpty()) { logger.warn("Broadcast transaction {} has failed, no connection.", txID); 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 b5f7d97726e..4321ad464bc 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 @@ -229,6 +229,8 @@ public static void clearParam() { PARAMETER.dynamicConfigEnable = false; PARAMETER.dynamicConfigCheckInterval = 600; PARAMETER.allowTvmShangHai = 0; + PARAMETER.unsolidifiedBlockCheck = true; + PARAMETER.maxUnsolidifiedBlocks = 1000; } /** @@ -1181,6 +1183,14 @@ public static void setParam(final String[] args, final String confFileName) { config.hasPath(Constant.COMMITTEE_ALLOW_TVM_SHANGHAI) ? config .getInt(Constant.COMMITTEE_ALLOW_TVM_SHANGHAI) : 0; + PARAMETER.unsolidifiedBlockCheck = + !config.hasPath(Constant.UNSOLIDIFIED_BLOCK_CHECK) + || config.getBoolean(Constant.UNSOLIDIFIED_BLOCK_CHECK); + + PARAMETER.maxUnsolidifiedBlocks = + config.hasPath(Constant.MAX_UNSOLIDIFIED_BLOCKS) ? config + .getInt(Constant.MAX_UNSOLIDIFIED_BLOCKS) : 1000; + logConfig(); } diff --git a/framework/src/main/java/org/tron/core/net/TronNetDelegate.java b/framework/src/main/java/org/tron/core/net/TronNetDelegate.java index 5d09e67908d..cd9c8f01d8b 100644 --- a/framework/src/main/java/org/tron/core/net/TronNetDelegate.java +++ b/framework/src/main/java/org/tron/core/net/TronNetDelegate.java @@ -29,6 +29,7 @@ import org.tron.core.capsule.BlockCapsule.BlockId; import org.tron.core.capsule.PbftSignCapsule; import org.tron.core.capsule.TransactionCapsule; +import org.tron.core.config.args.Args; import org.tron.core.db.Manager; import org.tron.core.exception.AccountResourceInsufficientException; import org.tron.core.exception.BadBlockException; @@ -98,6 +99,11 @@ public class TronNetDelegate { @Setter private volatile boolean exit = true; + private int maxUnsolidifiedBlocks = Args.getInstance().getMaxUnsolidifiedBlocks(); + + private boolean unsolidifiedBlockCheck + = Args.getInstance().isUnsolidifiedBlockCheck(); + private Cache freshBlockId = CacheBuilder.newBuilder() .maximumSize(blockIdCacheSize).expireAfterWrite(1, TimeUnit.HOURS) .recordStats().build(); @@ -365,4 +371,13 @@ public long getMaintenanceTimeInterval() { return chainBaseManager.getDynamicPropertiesStore().getMaintenanceTimeInterval(); } + public boolean isBlockUnsolidified() { + if (!unsolidifiedBlockCheck) { + return false; + } + long headNum = chainBaseManager.getHeadBlockNum(); + long solidNum = chainBaseManager.getSolidBlockId().getNum(); + return headNum - solidNum >= maxUnsolidifiedBlocks; + } + } diff --git a/framework/src/main/java/org/tron/core/net/messagehandler/InventoryMsgHandler.java b/framework/src/main/java/org/tron/core/net/messagehandler/InventoryMsgHandler.java index 02b04d73b32..a8ad8d0ec73 100644 --- a/framework/src/main/java/org/tron/core/net/messagehandler/InventoryMsgHandler.java +++ b/framework/src/main/java/org/tron/core/net/messagehandler/InventoryMsgHandler.java @@ -43,6 +43,7 @@ public void processMessage(PeerConnection peer, TronMessage msg) { } private boolean check(PeerConnection peer, InventoryMessage inventoryMessage) { + InventoryType type = inventoryMessage.getInventoryType(); int size = inventoryMessage.getHashList().size(); @@ -52,6 +53,12 @@ private boolean check(PeerConnection peer, InventoryMessage inventoryMessage) { return false; } + if (type.equals(InventoryType.TRX) && tronNetDelegate.isBlockUnsolidified()) { + logger.warn("Drop inv: {} size: {} from Peer {}, block unsolidified", + type, size, peer.getInetAddress()); + return false; + } + if (type.equals(InventoryType.TRX) && transactionsMsgHandler.isBusy()) { logger.warn("Drop inv: {} size: {} from Peer {}, transactionsMsgHandler is busy", type, size, peer.getInetAddress()); 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 56e418be7c1..59b684b338f 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 @@ -94,6 +94,8 @@ public void get() { Assert.assertEquals(0, parameter.getActiveNodes().size()); Assert.assertEquals(30, parameter.getMaxConnections()); Assert.assertEquals(43, parameter.getNodeP2pVersion()); + Assert.assertEquals(1000, parameter.getMaxUnsolidifiedBlocks()); + Assert.assertEquals(true, parameter.isUnsolidifiedBlockCheck()); //Assert.assertEquals(30, args.getSyncNodeCount()); // gRPC network configs checking diff --git a/framework/src/test/java/org/tron/core/net/TronNetDelegateTest.java b/framework/src/test/java/org/tron/core/net/TronNetDelegateTest.java new file mode 100644 index 00000000000..c1ac3df1599 --- /dev/null +++ b/framework/src/test/java/org/tron/core/net/TronNetDelegateTest.java @@ -0,0 +1,51 @@ +package org.tron.core.net; + +import static org.mockito.Mockito.mock; + +import java.lang.reflect.Field; +import org.junit.Assert; +import org.junit.Test; +import org.mockito.Mockito; +import org.tron.common.parameter.CommonParameter; +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.config.args.Args; + +public class TronNetDelegateTest { + + @Test + public void test() throws Exception { + Args.setParam(new String[] {"-w"}, Constant.TEST_CONF); + CommonParameter parameter = Args.getInstance(); + Args.logConfig(); + + BlockCapsule.BlockId blockId = new BlockCapsule.BlockId(Sha256Hash.ZERO_HASH, 10000L); + + TronNetDelegate tronNetDelegate = new TronNetDelegate(); + + ChainBaseManager chainBaseManager = mock(ChainBaseManager.class); + Mockito.when(chainBaseManager.getHeadBlockNum()).thenReturn(10000L); + Mockito.when(chainBaseManager.getSolidBlockId()).thenReturn(blockId); + + Field field = tronNetDelegate.getClass().getDeclaredField("chainBaseManager"); + field.setAccessible(true); + field.set(tronNetDelegate, chainBaseManager); + + Assert.assertTrue(!tronNetDelegate.isBlockUnsolidified()); + + blockId = new BlockCapsule.BlockId(Sha256Hash.ZERO_HASH, 1L); + Mockito.when(chainBaseManager.getSolidBlockId()).thenReturn(blockId); + Assert.assertTrue(tronNetDelegate.isBlockUnsolidified()); + + parameter.setUnsolidifiedBlockCheck(false); + tronNetDelegate = new TronNetDelegate(); + + field = tronNetDelegate.getClass().getDeclaredField("unsolidifiedBlockCheck"); + field.setAccessible(true); + field.set(tronNetDelegate, false); + + Assert.assertTrue(!tronNetDelegate.isBlockUnsolidified()); + } +} diff --git a/framework/src/test/java/org/tron/core/net/messagehandler/InventoryMsgHandlerTest.java b/framework/src/test/java/org/tron/core/net/messagehandler/InventoryMsgHandlerTest.java index 97db6207b2a..7aba0251708 100644 --- a/framework/src/test/java/org/tron/core/net/messagehandler/InventoryMsgHandlerTest.java +++ b/framework/src/test/java/org/tron/core/net/messagehandler/InventoryMsgHandlerTest.java @@ -1,10 +1,16 @@ package org.tron.core.net.messagehandler; +import static org.mockito.Mockito.mock; + import java.lang.reflect.Field; import java.net.InetAddress; import java.net.InetSocketAddress; import java.util.ArrayList; import org.junit.Test; +import org.mockito.Mockito; +import org.tron.core.Constant; +import org.tron.core.config.args.Args; +import org.tron.core.net.TronNetDelegate; import org.tron.core.net.message.adv.InventoryMessage; import org.tron.core.net.peer.PeerConnection; import org.tron.p2p.connection.Channel; @@ -12,10 +18,12 @@ public class InventoryMsgHandlerTest { - private InventoryMsgHandler handler = new InventoryMsgHandler(); - @Test public void testProcessMessage() throws Exception { + InventoryMsgHandler handler = new InventoryMsgHandler(); + Args.setParam(new String[] {"-w"}, Constant.TEST_CONF); + Args.logConfig(); + InventoryMessage msg = new InventoryMessage(new ArrayList<>(), InventoryType.TRX); PeerConnection peer = new PeerConnection(); peer.setChannel(getChannel("1.0.0.3", 1000)); @@ -31,6 +39,16 @@ public void testProcessMessage() throws Exception { peer.setNeedSyncFromUs(true); handler.processMessage(peer, msg); + peer.setNeedSyncFromUs(false); + + TronNetDelegate tronNetDelegate = mock(TronNetDelegate.class); + Mockito.when(tronNetDelegate.isBlockUnsolidified()).thenReturn(true); + + Field field = handler.getClass().getDeclaredField("tronNetDelegate"); + field.setAccessible(true); + field.set(handler, tronNetDelegate); + + handler.processMessage(peer, msg); } private Channel getChannel(String host, int port) throws Exception { diff --git a/protocol/src/main/protos/api/api.proto b/protocol/src/main/protos/api/api.proto index 9a7534cf6ce..2505fa48d6f 100644 --- a/protocol/src/main/protos/api/api.proto +++ b/protocol/src/main/protos/api/api.proto @@ -1051,6 +1051,8 @@ message Return { SERVER_BUSY = 9; NO_CONNECTION = 10; NOT_ENOUGH_EFFECTIVE_CONNECTION = 11; + BLOCK_UNSOLIDIFIED = 12; + OTHER_ERROR = 20; }