diff --git a/framework/src/main/java/org/tron/core/Wallet.java b/framework/src/main/java/org/tron/core/Wallet.java index 0482643d8d0..266c16e052f 100755 --- a/framework/src/main/java/org/tron/core/Wallet.java +++ b/framework/src/main/java/org/tron/core/Wallet.java @@ -24,6 +24,7 @@ import static org.tron.common.utils.Commons.getAssetIssueStoreFinal; import static org.tron.common.utils.Commons.getExchangeStoreFinal; import static org.tron.common.utils.WalletUtil.isConstant; +import static org.tron.core.Constant.PER_SIGN_LENGTH; import static org.tron.core.capsule.utils.TransactionUtil.buildInternalTransaction; import static org.tron.core.config.Parameter.ChainConstant.BLOCK_PRODUCED_INTERVAL; import static org.tron.core.config.Parameter.ChainConstant.TRX_PRECISION; @@ -505,6 +506,16 @@ public GrpcAPI.Return broadcastTransaction(Transaction signedTransaction) { trx.setTime(System.currentTimeMillis()); Sha256Hash txID = trx.getTransactionId(); try { + for (ByteString sig : signedTransaction.getSignatureList()) { + if (sig.size() != PER_SIGN_LENGTH) { + String info = "Signature size is " + sig.size(); + logger.warn("Broadcast transaction {} has failed, {}.", txID, info); + return builder.setResult(false).setCode(response_code.SIGERROR) + .setMessage(ByteString.copyFromUtf8("Validate signature error: " + info)) + .build(); + } + } + if (tronNetDelegate.isBlockUnsolidified()) { logger.warn("Broadcast transaction {} has failed, block unsolidified.", txID); return builder.setResult(false).setCode(response_code.BLOCK_UNSOLIDIFIED) diff --git a/framework/src/main/java/org/tron/core/net/messagehandler/TransactionsMsgHandler.java b/framework/src/main/java/org/tron/core/net/messagehandler/TransactionsMsgHandler.java index e153e21f331..8d477762f14 100644 --- a/framework/src/main/java/org/tron/core/net/messagehandler/TransactionsMsgHandler.java +++ b/framework/src/main/java/org/tron/core/net/messagehandler/TransactionsMsgHandler.java @@ -1,5 +1,8 @@ package org.tron.core.net.messagehandler; +import static org.tron.core.Constant.PER_SIGN_LENGTH; + +import com.google.protobuf.ByteString; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -142,6 +145,12 @@ private void check(PeerConnection peer, TransactionsMessage msg) throws P2pExcep throw new P2pException(TypeEnum.BAD_TRX, "tx " + item.getHash() + " contract size should be greater than 0"); } + for (ByteString sig : trx.getSignatureList()) { + if (sig.size() != PER_SIGN_LENGTH) { + throw new P2pException(TypeEnum.BAD_TRX, + "tx " + item.getHash() + " signature size is " + sig.size()); + } + } } } 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 61ae6326e9f..2f116d77ea2 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 @@ -1,5 +1,7 @@ package org.tron.core.net.service.relay; +import static org.tron.core.Constant.PER_SIGN_LENGTH; + import com.google.protobuf.ByteString; import java.net.InetSocketAddress; import java.util.Arrays; @@ -150,6 +152,12 @@ public boolean checkHelloMessage(HelloMessage message, Channel channel) { return false; } + if (msg.getSignature().size() != PER_SIGN_LENGTH) { + logger.warn("HelloMessage from {}, signature size is {}.", + channel.getInetAddress(), msg.getSignature().size()); + return false; + } + boolean flag; try { Sha256Hash hash = Sha256Hash.of(CommonParameter diff --git a/framework/src/test/java/org/tron/core/WalletMockTest.java b/framework/src/test/java/org/tron/core/WalletMockTest.java index 3e0c1a4461d..595306b9aaf 100644 --- a/framework/src/test/java/org/tron/core/WalletMockTest.java +++ b/framework/src/test/java/org/tron/core/WalletMockTest.java @@ -164,6 +164,47 @@ public void testCreateTransactionCapsuleWithoutValidateWithTimeout() } + @Test + public void testBroadcastTxInvalidSigLength() throws Exception { + Wallet wallet = new Wallet(); + TronNetDelegate tronNetDelegateMock = mock(TronNetDelegate.class); + Field field = wallet.getClass().getDeclaredField("tronNetDelegate"); + field.setAccessible(true); + field.set(wallet, tronNetDelegateMock); + + // signature shorter than 65 bytes → SIGERROR + Protocol.Transaction shortSig = Protocol.Transaction.newBuilder() + .addSignature(ByteString.copyFrom(new byte[64])) + .build(); + GrpcAPI.Return ret = wallet.broadcastTransaction(shortSig); + assertEquals(GrpcAPI.Return.response_code.SIGERROR, ret.getCode()); + + // signature longer than 65 bytes → SIGERROR + Protocol.Transaction longSig = Protocol.Transaction.newBuilder() + .addSignature(ByteString.copyFrom(new byte[66])) + .build(); + ret = wallet.broadcastTransaction(longSig); + assertEquals(GrpcAPI.Return.response_code.SIGERROR, ret.getCode()); + + // empty signature → SIGERROR + Protocol.Transaction emptySig = Protocol.Transaction.newBuilder() + .addSignature(ByteString.EMPTY) + .build(); + ret = wallet.broadcastTransaction(emptySig); + assertEquals(GrpcAPI.Return.response_code.SIGERROR, ret.getCode()); + + // tronNetDelegate must not be consulted because the request is rejected up front + Mockito.verify(tronNetDelegateMock, Mockito.never()).isBlockUnsolidified(); + + // 65-byte signature passes the length check and proceeds to downstream logic + when(tronNetDelegateMock.isBlockUnsolidified()).thenReturn(true); + Protocol.Transaction validSig = Protocol.Transaction.newBuilder() + .addSignature(ByteString.copyFrom(new byte[65])) + .build(); + ret = wallet.broadcastTransaction(validSig); + assertEquals(GrpcAPI.Return.response_code.BLOCK_UNSOLIDIFIED, ret.getCode()); + } + @Test public void testBroadcastTransactionBlockUnsolidified() throws Exception { Wallet wallet = new Wallet(); diff --git a/framework/src/test/java/org/tron/core/net/messagehandler/TransactionsMsgHandlerTest.java b/framework/src/test/java/org/tron/core/net/messagehandler/TransactionsMsgHandlerTest.java index abe69e76ff2..9faac63914a 100644 --- a/framework/src/test/java/org/tron/core/net/messagehandler/TransactionsMsgHandlerTest.java +++ b/framework/src/test/java/org/tron/core/net/messagehandler/TransactionsMsgHandlerTest.java @@ -337,6 +337,75 @@ public void testDuplicateTransactionRejected() throws Exception { } } + @Test + public void testInvalidSigLength() throws Exception { + TransactionsMsgHandler handler = new TransactionsMsgHandler(); + handler.init(); + try { + PeerConnection peer = Mockito.mock(PeerConnection.class); + + BalanceContract.TransferContract transferContract = BalanceContract.TransferContract + .newBuilder() + .setAmount(10) + .setOwnerAddress(ByteString.copyFrom(ByteArray.fromHexString("121212a9cf"))) + .setToAddress(ByteString.copyFrom(ByteArray.fromHexString("232323a9cf"))) + .build(); + + // signature shorter than 65 bytes → BAD_TRX + Protocol.Transaction shortSigTrx = Protocol.Transaction.newBuilder() + .setRawData(Protocol.Transaction.raw.newBuilder() + .addContract(Protocol.Transaction.Contract.newBuilder() + .setType(Protocol.Transaction.Contract.ContractType.TransferContract) + .setParameter(Any.pack(transferContract)).build()) + .build()) + .addSignature(ByteString.copyFrom(new byte[64])) + .build(); + + List shortList = new ArrayList<>(); + shortList.add(shortSigTrx); + stubAdvInvRequest(peer, new TransactionsMessage(shortList)); + P2pException shortEx = Assert.assertThrows(P2pException.class, + () -> handler.processMessage(peer, new TransactionsMessage(shortList))); + Assert.assertEquals(TypeEnum.BAD_TRX, shortEx.getType()); + + // signature longer than 65 bytes → BAD_TRX + Protocol.Transaction longSigTrx = Protocol.Transaction.newBuilder() + .setRawData(Protocol.Transaction.raw.newBuilder() + .setRefBlockNum(1) + .addContract(Protocol.Transaction.Contract.newBuilder() + .setType(Protocol.Transaction.Contract.ContractType.TransferContract) + .setParameter(Any.pack(transferContract)).build()) + .build()) + .addSignature(ByteString.copyFrom(new byte[66])) + .build(); + + List longList = new ArrayList<>(); + longList.add(longSigTrx); + stubAdvInvRequest(peer, new TransactionsMessage(longList)); + P2pException longEx = Assert.assertThrows(P2pException.class, + () -> handler.processMessage(peer, new TransactionsMessage(longList))); + Assert.assertEquals(TypeEnum.BAD_TRX, longEx.getType()); + + // exactly 65 bytes → passes the length check (no P2pException from check) + Protocol.Transaction validSigTrx = Protocol.Transaction.newBuilder() + .setRawData(Protocol.Transaction.raw.newBuilder() + .setRefBlockNum(2) + .addContract(Protocol.Transaction.Contract.newBuilder() + .setType(Protocol.Transaction.Contract.ContractType.TransferContract) + .setParameter(Any.pack(transferContract)).build()) + .build()) + .addSignature(ByteString.copyFrom(new byte[65])) + .build(); + + List validList = new ArrayList<>(); + validList.add(validSigTrx); + stubAdvInvRequest(peer, new TransactionsMessage(validList)); + handler.processMessage(peer, new TransactionsMessage(validList)); + } finally { + handler.close(); + } + } + @Test public void testIsBusyWithCachedTransactions() throws Exception { TransactionsMsgHandler handler = new TransactionsMsgHandler(); 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 8585244b941..8a63ff8a887 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 @@ -220,6 +220,30 @@ private void testCheckHelloMessage() { boolean res = service.checkHelloMessage(helloMessage, c1); Assert.assertTrue(res); + + HelloMessage shortSigMsg = new HelloMessage(node, System.currentTimeMillis(), + ChainBaseManager.getChainBaseManager()); + shortSigMsg.setHelloMessage(shortSigMsg.getHelloMessage().toBuilder() + .setAddress(address) + .setSignature(ByteString.copyFrom(new byte[64])) + .build()); + Assert.assertFalse(service.checkHelloMessage(shortSigMsg, c1)); + + HelloMessage longSigMsg = new HelloMessage(node, System.currentTimeMillis(), + ChainBaseManager.getChainBaseManager()); + longSigMsg.setHelloMessage(longSigMsg.getHelloMessage().toBuilder() + .setAddress(address) + .setSignature(ByteString.copyFrom(new byte[66])) + .build()); + Assert.assertFalse(service.checkHelloMessage(longSigMsg, c1)); + + HelloMessage emptySigMsg = new HelloMessage(node, System.currentTimeMillis(), + ChainBaseManager.getChainBaseManager()); + emptySigMsg.setHelloMessage(emptySigMsg.getHelloMessage().toBuilder() + .setAddress(address) + .setSignature(ByteString.EMPTY) + .build()); + Assert.assertFalse(service.checkHelloMessage(emptySigMsg, c1)); } catch (Exception e) { logger.info("", e); assert false;