diff --git a/actuator/src/main/java/org/tron/core/utils/TransactionUtil.java b/actuator/src/main/java/org/tron/core/utils/TransactionUtil.java index 53d6caf5691..63852a709a7 100644 --- a/actuator/src/main/java/org/tron/core/utils/TransactionUtil.java +++ b/actuator/src/main/java/org/tron/core/utils/TransactionUtil.java @@ -225,7 +225,8 @@ public TransactionSignWeight getTransactionSignWeight(Transaction trx) { List approveList = new ArrayList<>(); long currentWeight = TransactionCapsule.checkWeight(permission, trx.getSignatureList(), Sha256Hash.hash(CommonParameter.getInstance() - .isECKeyCryptoEngine(), trx.getRawData().toByteArray()), approveList); + .isECKeyCryptoEngine(), trx.getRawData().toByteArray()), approveList, + chainBaseManager.getDynamicPropertiesStore()); tswBuilder.addAllApprovedList(approveList); tswBuilder.setCurrentWeight(currentWeight); } diff --git a/chainbase/src/main/java/org/tron/core/capsule/BlockCapsule.java b/chainbase/src/main/java/org/tron/core/capsule/BlockCapsule.java index 34b7853d4d1..606feea19f2 100755 --- a/chainbase/src/main/java/org/tron/core/capsule/BlockCapsule.java +++ b/chainbase/src/main/java/org/tron/core/capsule/BlockCapsule.java @@ -185,9 +185,14 @@ private Sha256Hash getRawHash() { public boolean validateSignature(DynamicPropertiesStore dynamicPropertiesStore, AccountStore accountStore) throws ValidateSignatureException { try { + ByteString witnessSig = block.getBlockHeader().getWitnessSignature(); + if (!SignUtils.isValidLength(witnessSig.size(), + dynamicPropertiesStore.isAllowTvmOsaka())) { + throw new ValidateSignatureException( + "Witness signature size is " + witnessSig.size()); + } byte[] sigAddress = SignUtils.signatureToAddress(getRawHash().getBytes(), - TransactionCapsule.getBase64FromByteString( - block.getBlockHeader().getWitnessSignature()), + TransactionCapsule.getBase64FromByteString(witnessSig), CommonParameter.getInstance().isECKeyCryptoEngine()); byte[] witnessAccountAddress = block.getBlockHeader().getRawData().getWitnessAddress() .toByteArray(); 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 bb4b70cde1b..e027d54b8d9 100755 --- a/chainbase/src/main/java/org/tron/core/capsule/TransactionCapsule.java +++ b/chainbase/src/main/java/org/tron/core/capsule/TransactionCapsule.java @@ -33,6 +33,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.Objects; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; @@ -54,6 +55,7 @@ import org.tron.common.utils.Sha256Hash; import org.tron.core.actuator.TransactionFactory; import org.tron.core.config.Parameter; +import org.tron.core.config.Parameter.ChainConstant; import org.tron.core.db.TransactionContext; import org.tron.core.db.TransactionTrace; import org.tron.core.exception.BadItemException; @@ -230,8 +232,9 @@ public static long getWeight(Permission permission, byte[] address) { * @see ForkController#init(org.tron.core.ChainBaseManager) */ public static long checkWeight(Permission permission, List sigs, byte[] hash, - List approveList) + List approveList, DynamicPropertiesStore dynamicPropertiesStore) throws SignatureException, PermissionException, SignatureFormatException { + Objects.requireNonNull(dynamicPropertiesStore, "dynamicPropertiesStore"); long currentWeight = 0; if (sigs.size() > permission.getKeysCount()) { throw new PermissionException( @@ -240,9 +243,9 @@ public static long checkWeight(Permission permission, List sigs, byt } HashMap addMap = new HashMap(); for (ByteString sig : sigs) { - if (sig.size() < 65) { - throw new SignatureFormatException( - "Signature size is " + sig.size()); + if (!SignUtils.isValidLength(sig.size(), + dynamicPropertiesStore.isAllowTvmOsaka())) { + throw new SignatureFormatException("Signature size is " + sig.size()); } String base64 = TransactionCapsule.getBase64FromByteString(sig); byte[] address = SignUtils @@ -487,7 +490,8 @@ public static boolean validateSignature(Transaction transaction, throw new PermissionException("permission isn't exit"); } checkPermission(permissionId, permission, contract); - long weight = checkWeight(permission, transaction.getSignatureList(), hash, null); + long weight = checkWeight(permission, transaction.getSignatureList(), hash, null, + dynamicPropertiesStore); if (weight >= permission.getThreshold()) { return true; } @@ -583,7 +587,8 @@ public void sign(byte[] privateKey) { this.transaction = this.transaction.toBuilder().addSignature(sig).build(); } - public void addSign(byte[] privateKey, AccountStore accountStore) + public void addSign(byte[] privateKey, AccountStore accountStore, + DynamicPropertiesStore dynamicPropertiesStore) throws PermissionException, SignatureException, SignatureFormatException { Transaction.Contract contract = this.transaction.getRawData().getContract(0); int permissionId = contract.getPermissionId(); @@ -604,7 +609,7 @@ public void addSign(byte[] privateKey, AccountStore accountStore) if (this.transaction.getSignatureCount() > 0) { checkWeight(permission, this.transaction.getSignatureList(), this.getTransactionId().getBytes(), - approveList); + approveList, dynamicPropertiesStore); if (approveList.contains(ByteString.copyFrom(address))) { throw new PermissionException(encode58Check(address) + " had signed!"); } @@ -620,8 +625,9 @@ public void addSign(byte[] privateKey, AccountStore accountStore) .signHash(getTransactionId().getBytes()))); this.transaction = this.transaction.toBuilder().addSignature(sig).build(); } - - private static void checkPermission(int permissionId, Permission permission, Transaction.Contract contract) throws PermissionException { + + private static void checkPermission(int permissionId, Permission permission, + Transaction.Contract contract) throws PermissionException { if (permissionId != 0) { if (permission.getType() != PermissionType.Active) { throw new PermissionException("Permission type is error"); @@ -703,7 +709,7 @@ public boolean validateSignature(AccountStore accountStore, } } isVerified = true; - } + } return true; } 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 0f74f20d379..29c110fe3e0 100644 --- a/chainbase/src/main/java/org/tron/core/store/DynamicPropertiesStore.java +++ b/chainbase/src/main/java/org/tron/core/store/DynamicPropertiesStore.java @@ -3011,6 +3011,10 @@ public void saveAllowTvmOsaka(long value) { this.put(ALLOW_TVM_OSAKA, new BytesCapsule(ByteArray.fromLong(value))); } + public boolean isAllowTvmOsaka() { + return getAllowTvmOsaka() == 1L; + } + public long getAllowTvmPrague() { return Optional.ofNullable(getUnchecked(ALLOW_TVM_PRAGUE)) .map(BytesCapsule::getData) 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 5349ef8d875..53194c4596a 100644 --- a/common/src/main/java/org/tron/core/config/Parameter.java +++ b/common/src/main/java/org/tron/core/config/Parameter.java @@ -69,6 +69,11 @@ public class ChainConstant { public static final int MAX_VOTE_NUMBER = 30; public static final int SOLIDIFIED_THRESHOLD = 70; // 70% public static final int PRIVATE_KEY_LENGTH = 64; + public static final int MIN_SIGNATURE_SIZE = 65; + // Canonical ECDSA signature is 65 bytes (r||s||v). 68 = 65 + up to 3 trailing + // padding bytes; this window accommodates historical non-canonical encodings + // observed on chain. Long-term goal is to tighten this back to a strict 65. + public static final int MAX_SIGNATURE_SIZE = 68; public static final int BLOCK_SIZE = 2_000_000; public static final long CLOCK_MAX_DELAY = 3600000; // 3600 * 1000 ms public static final int BLOCK_PRODUCE_TIMEOUT_PERCENT = 50; // 50% diff --git a/consensus/src/main/java/org/tron/consensus/pbft/PbftMessageHandle.java b/consensus/src/main/java/org/tron/consensus/pbft/PbftMessageHandle.java index 523ffac4d61..ae90083f3b4 100644 --- a/consensus/src/main/java/org/tron/consensus/pbft/PbftMessageHandle.java +++ b/consensus/src/main/java/org/tron/consensus/pbft/PbftMessageHandle.java @@ -129,7 +129,7 @@ public void onPrePrepare(PbftMessage message) { PbftMessage paMessage = message.buildPrePareMessage(miner); forwardMessage(paMessage); try { - paMessage.analyzeSignature(); + paMessage.analyzeSignature(chainBaseManager.getDynamicPropertiesStore()); } catch (SignatureException e) { logger.error("", e); } @@ -175,7 +175,7 @@ public synchronized void onPrepare(PbftMessage message) { doneMsg.put(message.getNo(), cmMessage); forwardMessage(cmMessage); try { - cmMessage.analyzeSignature(); + cmMessage.analyzeSignature(chainBaseManager.getDynamicPropertiesStore()); } catch (SignatureException e) { logger.error("", e); } @@ -316,4 +316,4 @@ public void run() { } }, 10, 1000); } -} \ No newline at end of file +} diff --git a/consensus/src/main/java/org/tron/consensus/pbft/message/PbftBaseMessage.java b/consensus/src/main/java/org/tron/consensus/pbft/message/PbftBaseMessage.java index 4eb61f3e22e..cedabd71e2e 100644 --- a/consensus/src/main/java/org/tron/consensus/pbft/message/PbftBaseMessage.java +++ b/consensus/src/main/java/org/tron/consensus/pbft/message/PbftBaseMessage.java @@ -1,17 +1,20 @@ package org.tron.consensus.pbft.message; +import com.google.protobuf.ByteString; import com.google.protobuf.InvalidProtocolBufferException; import java.io.IOException; import java.security.SignatureException; import java.util.stream.Collectors; import org.bouncycastle.util.encoders.Hex; import org.tron.common.crypto.ECKey; +import org.tron.common.crypto.SignUtils; import org.tron.common.overlay.message.Message; import org.tron.common.utils.ByteUtil; import org.tron.common.utils.Sha256Hash; import org.tron.common.utils.StringUtil; import org.tron.core.capsule.TransactionCapsule; import org.tron.core.exception.P2pException; +import org.tron.core.store.DynamicPropertiesStore; import org.tron.protos.Protocol.PBFTMessage; import org.tron.protos.Protocol.PBFTMessage.DataType; import org.tron.protos.Protocol.SRL; @@ -94,10 +97,16 @@ public DataType getDataType() { public abstract String getNo(); - public void analyzeSignature() throws SignatureException { + public void analyzeSignature(DynamicPropertiesStore dynamicPropertiesStore) + throws SignatureException { + ByteString signature = getPbftMessage().getSignature(); + if (!SignUtils.isValidLength(signature.size(), + dynamicPropertiesStore.isAllowTvmOsaka())) { + throw new SignatureException("PBFT signature size is " + signature.size()); + } byte[] hash = Sha256Hash.hash(true, getPbftMessage().getRawData().toByteArray()); publicKey = ECKey.signatureToAddress(hash, TransactionCapsule - .getBase64FromByteString(getPbftMessage().getSignature())); + .getBase64FromByteString(signature)); } @Override diff --git a/crypto/src/main/java/org/tron/common/crypto/SignUtils.java b/crypto/src/main/java/org/tron/common/crypto/SignUtils.java index b921d548e8b..6d05decec02 100644 --- a/crypto/src/main/java/org/tron/common/crypto/SignUtils.java +++ b/crypto/src/main/java/org/tron/common/crypto/SignUtils.java @@ -5,9 +5,15 @@ import org.tron.common.crypto.ECKey.ECDSASignature; import org.tron.common.crypto.sm2.SM2; import org.tron.common.crypto.sm2.SM2.SM2Signature; +import org.tron.core.config.Parameter.ChainConstant; public class SignUtils { + public static boolean isValidLength(int size, boolean osakaAllowed) { + return size >= ChainConstant.MIN_SIGNATURE_SIZE + && (!osakaAllowed || size <= ChainConstant.MAX_SIGNATURE_SIZE); + } + public static SignInterface getGeneratedRandomSign( SecureRandom secureRandom, boolean isECKeyCryptoEngine) { if (isECKeyCryptoEngine) { diff --git a/framework/src/main/java/org/tron/core/Wallet.java b/framework/src/main/java/org/tron/core/Wallet.java index 0482643d8d0..31ba6518b02 100755 --- a/framework/src/main/java/org/tron/core/Wallet.java +++ b/framework/src/main/java/org/tron/core/Wallet.java @@ -160,6 +160,7 @@ import org.tron.core.capsule.VotesCapsule; import org.tron.core.capsule.WitnessCapsule; import org.tron.core.capsule.utils.MarketUtils; +import org.tron.core.config.Parameter.ChainConstant; import org.tron.core.config.args.Args; import org.tron.core.db.BandwidthProcessor; import org.tron.core.db.BlockIndexStore; @@ -644,7 +645,8 @@ public TransactionApprovedList getTransactionApprovedList(Transaction trx) { byte[] hash = Sha256Hash.hash(CommonParameter .getInstance().isECKeyCryptoEngine(), trx.getRawData().toByteArray()); for (ByteString sig : trx.getSignatureList()) { - if (sig.size() < 65) { + if (!SignUtils.isValidLength(sig.size(), + chainBaseManager.getDynamicPropertiesStore().isAllowTvmOsaka())) { throw new SignatureFormatException( "Signature size is " + sig.size()); } diff --git a/framework/src/main/java/org/tron/core/net/messagehandler/PbftDataSyncHandler.java b/framework/src/main/java/org/tron/core/net/messagehandler/PbftDataSyncHandler.java index d66fa6d41f7..766179dd66f 100644 --- a/framework/src/main/java/org/tron/core/net/messagehandler/PbftDataSyncHandler.java +++ b/framework/src/main/java/org/tron/core/net/messagehandler/PbftDataSyncHandler.java @@ -19,6 +19,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.tron.common.crypto.ECKey; +import org.tron.common.crypto.SignUtils; import org.tron.common.es.ExecutorServiceManager; import org.tron.common.utils.ByteArray; import org.tron.common.utils.Sha256Hash; @@ -122,20 +123,17 @@ private void processPBFTCommitMessage(PbftCommitMessage pbftCommitMessage) { private boolean validPbftSign(Raw raw, List srSignList, List currentSrList) { //valid sr list + if (srSignList.size() < Param.getInstance().getAgreeNodeCount()) { + return false; + } if (srSignList.size() != 0) { - Set srSignSet = new ConcurrentSet(); - srSignSet.addAll(srSignList); - if (srSignSet.size() < Param.getInstance().getAgreeNodeCount()) { - logger.error("sr sign count {} < sr count * 2/3 + 1 == {}", srSignSet.size(), - Param.getInstance().getAgreeNodeCount()); - return false; - } byte[] dataHash = Sha256Hash.hash(true, raw.toByteArray()); Set srSet = Sets.newHashSet(currentSrList); + Set validSrAddressSet = new ConcurrentSet(); List> futureList = new ArrayList<>(); for (ByteString sign : srSignList) { futureList.add(executorService.submit( - new ValidPbftSignTask(raw.getViewN(), srSignSet, dataHash, srSet, sign))); + new ValidPbftSignTask(raw.getViewN(), validSrAddressSet, dataHash, srSet, sign))); } for (Future future : futureList) { try { @@ -146,7 +144,9 @@ private boolean validPbftSign(Raw raw, List srSignList, logger.error("", e); } } - if (srSignSet.size() != 0) { + if (validSrAddressSet.size() < Param.getInstance().getAgreeNodeCount()) { + logger.error("sr sign count {} < sr count * 2/3 + 1 == {}", validSrAddressSet.size(), + Param.getInstance().getAgreeNodeCount()); return false; } } @@ -156,15 +156,15 @@ private boolean validPbftSign(Raw raw, List srSignList, private class ValidPbftSignTask implements Callable { private long viewN; - private Set srSignSet; + private Set validSrAddressSet; private byte[] dataHash; private Set srSet; private ByteString sign; - ValidPbftSignTask(long viewN, Set srSignSet, + ValidPbftSignTask(long viewN, Set validSrAddressSet, byte[] dataHash, Set srSet, ByteString sign) { this.viewN = viewN; - this.srSignSet = srSignSet; + this.validSrAddressSet = validSrAddressSet; this.dataHash = dataHash; this.srSet = srSet; this.sign = sign; @@ -173,6 +173,10 @@ private class ValidPbftSignTask implements Callable { @Override public Boolean call() throws Exception { try { + if (!SignUtils.isValidLength(sign.size(), + chainBaseManager.getDynamicPropertiesStore().isAllowTvmOsaka())) { + throw new SignatureException("PBFT signature size is " + sign.size()); + } byte[] srAddress = ECKey.signatureToAddress(dataHash, TransactionCapsule.getBase64FromByteString(sign)); if (!srSet.contains(ByteString.copyFrom(srAddress))) { @@ -180,7 +184,7 @@ public Boolean call() throws Exception { ByteArray.toHexString(srAddress)); return false; } - srSignSet.remove(sign); + validSrAddressSet.add(ByteString.copyFrom(srAddress)); } catch (SignatureException e) { logger.error("viewN {} valid sr list sign fail!", viewN, e); return false; diff --git a/framework/src/main/java/org/tron/core/net/messagehandler/PbftMsgHandler.java b/framework/src/main/java/org/tron/core/net/messagehandler/PbftMsgHandler.java index d086cc28b6c..c5287a2b27d 100644 --- a/framework/src/main/java/org/tron/core/net/messagehandler/PbftMsgHandler.java +++ b/framework/src/main/java/org/tron/core/net/messagehandler/PbftMsgHandler.java @@ -11,6 +11,7 @@ import org.tron.consensus.pbft.PbftManager; import org.tron.consensus.pbft.message.PbftBaseMessage; import org.tron.consensus.pbft.message.PbftMessage; +import org.tron.core.ChainBaseManager; import org.tron.core.config.args.Args; import org.tron.core.exception.P2pException; import org.tron.core.net.TronNetDelegate; @@ -32,6 +33,9 @@ public class PbftMsgHandler { @Autowired private TronNetDelegate tronNetDelegate; + @Autowired + private ChainBaseManager chainBaseManager; + public void processMessage(PeerConnection peer, PbftMessage msg) throws Exception { if (!tronNetDelegate.allowPBFT()) { return; @@ -50,7 +54,7 @@ public void processMessage(PeerConnection peer, PbftMessage msg) throws Exceptio && currentEpoch - msg.getEpoch() > expireEpoch) { return; } - msg.analyzeSignature(); + msg.analyzeSignature(chainBaseManager.getDynamicPropertiesStore()); String key = buildKey(msg); Lock lock = striped.get(key); try { @@ -79,4 +83,4 @@ private String buildKey(PbftBaseMessage msg) { return msg.getKey() + msg.getPbftMessage().getRawData().getMsgType().toString(); } -} \ No newline at end of file +} 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..4b405409997 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 @@ -152,10 +152,15 @@ public boolean checkHelloMessage(HelloMessage message, Channel channel) { boolean flag; try { + ByteString signature = msg.getSignature(); + if (!SignUtils.isValidLength(signature.size(), + chainBaseManager.getDynamicPropertiesStore().isAllowTvmOsaka())) { + return false; + } Sha256Hash hash = Sha256Hash.of(CommonParameter .getInstance().isECKeyCryptoEngine(), ByteArray.fromLong(msg.getTimestamp())); String sig = - TransactionCapsule.getBase64FromByteString(msg.getSignature()); + TransactionCapsule.getBase64FromByteString(signature); byte[] sigAddress = SignUtils.signatureToAddress(hash.getBytes(), sig, Args.getInstance().isECKeyCryptoEngine()); if (manager.getDynamicPropertiesStore().getAllowMultiSign() != 1) { 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 63ffe1b58ff..c2a9f701f32 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 @@ -32,6 +32,7 @@ import org.tron.core.exception.SignatureFormatException; import org.tron.core.services.http.JsonFormat; import org.tron.core.store.AccountStore; +import org.tron.core.store.DynamicPropertiesStore; import org.tron.protos.Protocol.Transaction; import org.tron.protos.Protocol.Transaction.Contract; import org.tron.protos.contract.AccountContract.AccountCreateContract; @@ -193,11 +194,12 @@ public static String getTransactionSign(String transaction, String priKey, } public static TransactionCapsule addTransactionSign(Transaction transaction, String priKey, - AccountStore accountStore) + AccountStore accountStore, + DynamicPropertiesStore dynamicPropertiesStore) throws PermissionException, SignatureException, SignatureFormatException { byte[] privateKey = ByteArray.fromHexString(priKey); TransactionCapsule trx = new TransactionCapsule(transaction); - trx.addSign(privateKey, accountStore); + trx.addSign(privateKey, accountStore, dynamicPropertiesStore); return trx; } 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 578f9f5ebed..fdd49988d6e 100755 --- a/framework/src/test/java/org/tron/core/actuator/ShieldedTransferActuatorTest.java +++ b/framework/src/test/java/org/tron/core/actuator/ShieldedTransferActuatorTest.java @@ -207,7 +207,8 @@ public void publicAddressToShieldedAddressSuccess() { //Add public address sign transactionCap = TransactionUtils.addTransactionSign(transactionCap.getInstance(), - ADDRESS_ONE_PRIVATE_KEY, dbManager.getAccountStore()); + ADDRESS_ONE_PRIVATE_KEY, dbManager.getAccountStore(), + dbManager.getDynamicPropertiesStore()); Assert.assertTrue(dbManager.pushTransaction(transactionCap)); } catch (Exception e) { @@ -235,7 +236,8 @@ public void publicAddressToPublicAddressAndZereValueOutputSuccess() { //Add public address sign transactionCap = TransactionUtils.addTransactionSign(transactionCap.getInstance(), - ADDRESS_ONE_PRIVATE_KEY, dbManager.getAccountStore()); + ADDRESS_ONE_PRIVATE_KEY, dbManager.getAccountStore(), + dbManager.getDynamicPropertiesStore()); Assert.assertTrue(dbManager.pushTransaction(transactionCap)); } catch (Exception e) { @@ -255,7 +257,7 @@ public void publicAddressToShieldedAddressInvalidSign() { //Add public address sign TransactionUtils.addTransactionSign(transactionCap.getInstance(), ADDRESS_TWO_PRIVATE_KEY, - dbManager.getAccountStore()); + dbManager.getAccountStore(), dbManager.getDynamicPropertiesStore()); Assert.assertTrue(false); } catch (PermissionException e) { Assert.assertTrue(e instanceof PermissionException); @@ -395,7 +397,8 @@ public void publicAddressToShieldedAddressNotConsumeBandwidth() { TransactionCapsule transactionCap = getPublicToShieldedTransaction(); //Add public address sign transactionCap = TransactionUtils.addTransactionSign(transactionCap.getInstance(), - ADDRESS_ONE_PRIVATE_KEY, dbManager.getAccountStore()); + ADDRESS_ONE_PRIVATE_KEY, dbManager.getAccountStore(), + dbManager.getDynamicPropertiesStore()); AccountCapsule accountCapsule = dbManager.getAccountStore().get(ByteArray.fromHexString(PUBLIC_ADDRESS_ONE)); @@ -983,7 +986,8 @@ public void publicToShieldAddressAndShieldToPublicAddressWithZoreValueSuccess() //Add public address sign transactionCapOne = TransactionUtils.addTransactionSign(transactionCapOne.getInstance(), - ADDRESS_ONE_PRIVATE_KEY, dbManager.getAccountStore()); + ADDRESS_ONE_PRIVATE_KEY, dbManager.getAccountStore(), + dbManager.getDynamicPropertiesStore()); Assert.assertTrue(dbManager.pushTransaction(transactionCapOne)); AccountCapsule accountCapsuleOne = @@ -1403,4 +1407,3 @@ public void shieldedTransferValidationWorksWhenApiDisabled() { } } } - diff --git a/framework/src/test/java/org/tron/core/actuator/utils/TransactionUtilTest.java b/framework/src/test/java/org/tron/core/actuator/utils/TransactionUtilTest.java index 15842bfa2c8..43c24f54d33 100644 --- a/framework/src/test/java/org/tron/core/actuator/utils/TransactionUtilTest.java +++ b/framework/src/test/java/org/tron/core/actuator/utils/TransactionUtilTest.java @@ -17,11 +17,14 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; +import javax.annotation.Resource; 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.TransactionSignWeight; +import org.tron.api.GrpcAPI.TransactionSignWeight.Result.response_code; import org.tron.common.BaseTest; import org.tron.common.TestConstants; import org.tron.common.utils.ByteArray; @@ -38,12 +41,16 @@ import org.tron.protos.Protocol.Transaction; import org.tron.protos.Protocol.Transaction.Contract.ContractType; import org.tron.protos.contract.BalanceContract.DelegateResourceContract; +import org.tron.protos.contract.BalanceContract.TransferContract; @Slf4j(topic = "capsule") public class TransactionUtilTest extends BaseTest { private static String OWNER_ADDRESS; + @Resource + private TransactionUtil transactionUtil; + /** * Init . */ @@ -452,4 +459,30 @@ public void testConcurrentToString() throws InterruptedException { } Assert.assertTrue(true); } + + @Test + public void getTransactionSignWeightPaddedSigOsakaRejected() { + byte[] owner = ByteArray.fromHexString(OWNER_ADDRESS); + TransferContract transferContract = TransferContract.newBuilder() + .setAmount(1L) + .setOwnerAddress(ByteString.copyFrom(owner)) + .setToAddress(ByteString.copyFrom(new byte[21])) + .build(); + byte[] paddedSig = new byte[69]; + paddedSig[64] = 27; + Transaction transaction = new TransactionCapsule( + transferContract, ContractType.TransferContract) + .getInstance().toBuilder() + .addSignature(ByteString.copyFrom(paddedSig)) + .build(); + + dbManager.getDynamicPropertiesStore().saveAllowTvmOsaka(1); + try { + TransactionSignWeight signWeight = transactionUtil.getTransactionSignWeight(transaction); + Assert.assertEquals(response_code.SIGNATURE_FORMAT_ERROR, + signWeight.getResult().getCode()); + } finally { + dbManager.getDynamicPropertiesStore().saveAllowTvmOsaka(0); + } + } } 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 ca0844c2c16..955a790aded 100644 --- a/framework/src/test/java/org/tron/core/capsule/BlockCapsuleTest.java +++ b/framework/src/test/java/org/tron/core/capsule/BlockCapsuleTest.java @@ -1,5 +1,8 @@ package org.tron.core.capsule; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + import com.google.protobuf.ByteString; import java.io.IOException; import java.util.ArrayList; @@ -21,6 +24,10 @@ import org.tron.core.config.args.Args; import org.tron.core.exception.BadBlockException; import org.tron.core.exception.BadItemException; +import org.tron.core.exception.ValidateSignatureException; +import org.tron.core.store.AccountStore; +import org.tron.core.store.DynamicPropertiesStore; +import org.tron.protos.Protocol.Block; import org.tron.protos.Protocol.Transaction.Contract.ContractType; import org.tron.protos.contract.BalanceContract.TransferContract; @@ -196,4 +203,86 @@ public void testConcurrentToString() throws InterruptedException { Assert.assertTrue(true); } + // ---- witness signature size enforcement tests ---- + + private BlockCapsule signedBlock() { + String key = PublicMethod.getRandomPrivateKey(); + LocalWitnesses lw = new LocalWitnesses(); + lw.setPrivateKeys(Arrays.asList(key)); + lw.initWitnessAccountAddress(null, true); + Args.setLocalWitnesses(lw); + + BlockCapsule block = new BlockCapsule(2, + Sha256Hash.wrap(ByteString.copyFrom(ByteArray.fromHexString( + "9938a342238077182498b464ac0292229938a342238077182498b464ac029222"))), + 9999L, + ByteString.copyFrom(lw.getWitnessAccountAddress())); + block.sign(ByteArray.fromHexString(key)); + return block; + } + + private BlockCapsule withPaddedWitnessSig(BlockCapsule src) { + byte[] original = src.getInstance().getBlockHeader() + .getWitnessSignature().toByteArray(); + // append one extra zero byte + byte[] padded = Arrays.copyOf(original, original.length + 1); + Block modified = src.getInstance().toBuilder() + .setBlockHeader(src.getInstance().getBlockHeader().toBuilder() + .setWitnessSignature(ByteString.copyFrom(padded))) + .build(); + return new BlockCapsule(modified); + } + + @Test + public void witnessSignaturePaddedOsakaRejected() throws Exception { + BlockCapsule paddedBlock = withPaddedWitnessSig(signedBlock()); + Assert.assertEquals(66, + paddedBlock.getInstance().getBlockHeader().getWitnessSignature().size()); + + DynamicPropertiesStore dps = mock(DynamicPropertiesStore.class); + when(dps.getAllowTvmOsaka()).thenReturn(1L); + AccountStore accountStore = mock(AccountStore.class); + + try { + paddedBlock.validateSignature(dps, accountStore); + Assert.fail("Expected ValidateSignatureException for padded witness sig post-Osaka"); + } catch (ValidateSignatureException e) { + Assert.assertTrue("Error must mention the sig size", + e.getMessage().contains("66")); + } + } + + @Test + public void witnessSignaturePaddedPreOsaka() throws Exception { + BlockCapsule paddedBlock = withPaddedWitnessSig(signedBlock()); + + DynamicPropertiesStore dps = mock(DynamicPropertiesStore.class); + when(dps.getAllowTvmOsaka()).thenReturn(0L); + when(dps.getAllowMultiSign()).thenReturn(0L); + AccountStore accountStore = mock(AccountStore.class); + + try { + paddedBlock.validateSignature(dps, accountStore); + // valid result is fine + } catch (ValidateSignatureException e) { + Assert.assertFalse("Size check must not fire before Osaka", + e.getMessage().contains("Witness signature size")); + } + } + + @Test(expected = ValidateSignatureException.class) + public void witnessSignatureShortRejected() throws Exception { + Block modified = blockCapsule0.getInstance().toBuilder() + .setBlockHeader(blockCapsule0.getInstance().getBlockHeader().toBuilder() + .setWitnessSignature(ByteString.copyFrom(new byte[64]))) + .build(); + BlockCapsule shortBlock = new BlockCapsule(modified); + + DynamicPropertiesStore dps = mock(DynamicPropertiesStore.class); + when(dps.getAllowTvmOsaka()).thenReturn(0L); + AccountStore accountStore = mock(AccountStore.class); + + shortBlock.validateSignature(dps, accountStore); + } + } diff --git a/framework/src/test/java/org/tron/core/capsule/TransactionCapsuleTest.java b/framework/src/test/java/org/tron/core/capsule/TransactionCapsuleTest.java index 9c2e004931e..43cb766f783 100644 --- a/framework/src/test/java/org/tron/core/capsule/TransactionCapsuleTest.java +++ b/framework/src/test/java/org/tron/core/capsule/TransactionCapsuleTest.java @@ -9,6 +9,7 @@ import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.read.ListAppender; import com.google.protobuf.ByteString; +import java.util.Collections; import java.util.List; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @@ -23,7 +24,11 @@ import org.tron.common.utils.StringUtil; import org.tron.core.Wallet; import org.tron.core.config.args.Args; +import org.tron.core.exception.SignatureFormatException; import org.tron.protos.Protocol.AccountType; +import org.tron.protos.Protocol.Key; +import org.tron.protos.Protocol.Permission; +import org.tron.protos.Protocol.Permission.PermissionType; import org.tron.protos.Protocol.Transaction; import org.tron.protos.Protocol.Transaction.Contract.ContractType; import org.tron.protos.Protocol.Transaction.Result; @@ -134,4 +139,75 @@ public void fastVerify() { capsuleLogger.setLevel(originalLevel); } } + + // ---- checkWeight signature-size enforcement tests ---- + + private static final byte[] DUMMY_HASH = new byte[32]; + + private static Permission singleKeyPermission() { + return Permission.newBuilder() + .setType(PermissionType.Owner) + .setThreshold(1) + .addKeys(Key.newBuilder() + .setAddress(ByteString.copyFrom(new byte[21])) + .setWeight(1)) + .build(); + } + + @Test(expected = SignatureFormatException.class) + public void checkWeightShortSigRejected() throws Exception { + List sigs = Collections.singletonList(ByteString.copyFrom(new byte[64])); + TransactionCapsule.checkWeight(singleKeyPermission(), sigs, DUMMY_HASH, null, + dbManager.getDynamicPropertiesStore()); + } + + @Test(expected = SignatureFormatException.class) + public void checkWeightPaddedSigOsakaRejected() throws Exception { + dbManager.getDynamicPropertiesStore().saveAllowTvmOsaka(1); + try { + List sigs = Collections.singletonList(ByteString.copyFrom(new byte[69])); + TransactionCapsule.checkWeight(singleKeyPermission(), sigs, DUMMY_HASH, null, + dbManager.getDynamicPropertiesStore()); + } finally { + dbManager.getDynamicPropertiesStore().saveAllowTvmOsaka(0); + } + } + + @Test + public void checkWeightPaddedSigPreOsaka() { + dbManager.getDynamicPropertiesStore().saveAllowTvmOsaka(0); + List sigs = Collections.singletonList(ByteString.copyFrom(new byte[69])); + try { + TransactionCapsule.checkWeight(singleKeyPermission(), sigs, DUMMY_HASH, null, + dbManager.getDynamicPropertiesStore()); + } catch (SignatureFormatException e) { + Assert.fail("Padded sig must not be rejected by size check before Osaka: " + e.getMessage()); + } catch (Exception e) { + // SignatureException / PermissionException after the size check — expected + } + } + + @Test(expected = NullPointerException.class) + public void checkWeightNullDynamicPropertiesStoreRejected() throws Exception { + List sigs = Collections.singletonList(ByteString.copyFrom(new byte[65])); + TransactionCapsule.checkWeight(singleKeyPermission(), sigs, DUMMY_HASH, null, null); + } + + @Test + public void checkWeightNormalSigOsaka() { + dbManager.getDynamicPropertiesStore().saveAllowTvmOsaka(1); + try { + byte[] validSizeSig = new byte[65]; + validSizeSig[64] = 27; // v = 27 (valid range) + List sigs = Collections.singletonList(ByteString.copyFrom(validSizeSig)); + TransactionCapsule.checkWeight(singleKeyPermission(), sigs, DUMMY_HASH, null, + dbManager.getDynamicPropertiesStore()); + } catch (SignatureFormatException e) { + Assert.fail("65-byte sig must not be rejected by size check post-Osaka: " + e.getMessage()); + } catch (Exception e) { + // crypto / permission error after size check — expected + } finally { + dbManager.getDynamicPropertiesStore().saveAllowTvmOsaka(0); + } + } } 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 e107979107a..93379e36d31 100644 --- a/framework/src/test/java/org/tron/core/db/TransactionExpireTest.java +++ b/framework/src/test/java/org/tron/core/db/TransactionExpireTest.java @@ -184,4 +184,77 @@ public void testTransactionApprovedList() { Assert.assertEquals(TransactionApprovedList.Result.response_code.COMPUTE_ADDRESS_ERROR, transactionApprovedList.getResult().getCode()); } + + @Test + public void testApprovedListPaddedSigOsakaRejected() { + initLocalWitness(); + byte[] address = Args.getLocalWitnesses().getWitnessAccountAddress(); + ByteString addressByte = ByteString.copyFrom(address); + + AccountCapsule accountCapsule = + new AccountCapsule(Protocol.Account.newBuilder().setAddress(addressByte).build()); + accountCapsule.setBalance(1000_000_000L); + dbManager.getChainBaseManager().getAccountStore() + .put(accountCapsule.createDbKey(), accountCapsule); + + TransferContract transferContract = TransferContract.newBuilder() + .setAmount(1L) + .setOwnerAddress(addressByte) + .setToAddress(ByteString.copyFrom(ByteArray.fromHexString( + Wallet.getAddressPreFixString() + "A389132D6639FBDA4FBC8B659264E6B7C90DB086"))) + .build(); + TransactionCapsule capsule = + new TransactionCapsule(transferContract, ContractType.TransferContract); + capsule.sign(ByteArray.fromHexString(Args.getLocalWitnesses().getPrivateKey())); + + // Replace the valid 65-byte sig with a 69-byte over-padded one (exceeds MAX_SIGNATURE_SIZE=68) + byte[] sig65 = capsule.getInstance().getSignature(0).toByteArray(); + byte[] sig69 = Arrays.copyOf(sig65, 69); + Transaction tx = capsule.getInstance().toBuilder() + .clearSignature().addSignature(ByteString.copyFrom(sig69)).build(); + + dbManager.getDynamicPropertiesStore().saveAllowTvmOsaka(1); + try { + TransactionApprovedList result = wallet.getTransactionApprovedList(tx); + Assert.assertEquals("Over-padded sig must be rejected post-Osaka", + TransactionApprovedList.Result.response_code.SIGNATURE_FORMAT_ERROR, + result.getResult().getCode()); + Assert.assertTrue(result.getResult().getMessage().contains("69")); + } finally { + dbManager.getDynamicPropertiesStore().saveAllowTvmOsaka(0); + } + } + + @Test + public void testApprovedListPaddedSigPreOsaka() { + initLocalWitness(); + byte[] address = Args.getLocalWitnesses().getWitnessAccountAddress(); + ByteString addressByte = ByteString.copyFrom(address); + AccountCapsule accountCapsule = + new AccountCapsule(Protocol.Account.newBuilder().setAddress(addressByte).build()); + accountCapsule.setBalance(1000_000_000L); + dbManager.getChainBaseManager().getAccountStore() + .put(accountCapsule.createDbKey(), accountCapsule); + + TransferContract transferContract = TransferContract.newBuilder() + .setAmount(1L) + .setOwnerAddress(addressByte) + .setToAddress(ByteString.copyFrom(ByteArray.fromHexString( + Wallet.getAddressPreFixString() + "A389132D6639FBDA4FBC8B659264E6B7C90DB086"))) + .build(); + TransactionCapsule capsule = + new TransactionCapsule(transferContract, ContractType.TransferContract); + capsule.sign(ByteArray.fromHexString(Args.getLocalWitnesses().getPrivateKey())); + + byte[] sig65 = capsule.getInstance().getSignature(0).toByteArray(); + byte[] sig66 = Arrays.copyOf(sig65, 66); + Transaction tx = capsule.getInstance().toBuilder() + .clearSignature().addSignature(ByteString.copyFrom(sig66)).build(); + + dbManager.getDynamicPropertiesStore().saveAllowTvmOsaka(0); + TransactionApprovedList result = wallet.getTransactionApprovedList(tx); + Assert.assertNotEquals("Padded sig must not be rejected by size check before Osaka", + TransactionApprovedList.Result.response_code.SIGNATURE_FORMAT_ERROR, + result.getResult().getCode()); + } } diff --git a/framework/src/test/java/org/tron/core/net/messagehandler/PbftDataSyncHandlerTest.java b/framework/src/test/java/org/tron/core/net/messagehandler/PbftDataSyncHandlerTest.java index 7f3ad8a71e2..54966dbd327 100644 --- a/framework/src/test/java/org/tron/core/net/messagehandler/PbftDataSyncHandlerTest.java +++ b/framework/src/test/java/org/tron/core/net/messagehandler/PbftDataSyncHandlerTest.java @@ -3,12 +3,21 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.google.protobuf.ByteString; import java.lang.reflect.Field; +import java.lang.reflect.Method; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; import java.util.Map; +import org.bouncycastle.util.encoders.Hex; import org.junit.Assert; import org.junit.Test; import org.mockito.Mockito; +import org.tron.common.crypto.SignInterface; +import org.tron.common.crypto.SignUtils; +import org.tron.common.utils.PublicMethod; import org.tron.common.utils.Sha256Hash; +import org.tron.consensus.base.Param; import org.tron.core.ChainBaseManager; import org.tron.core.capsule.BlockCapsule; import org.tron.core.capsule.PbftSignCapsule; @@ -55,4 +64,77 @@ public void testProcessMessage() throws Exception { Map map = new ObjectMapper().convertValue(field1.get(pbftDataSyncHandler), Map.class); Assert.assertFalse(map.containsKey(0)); } + + @Test + public void testValidPbftSignPaddedSigOsakaRejected() throws Exception { + PbftDataSyncHandler pbftDataSyncHandler = new PbftDataSyncHandler(); + DynamicPropertiesStore dynamicPropertiesStore = Mockito.mock(DynamicPropertiesStore.class); + ChainBaseManager chainBaseManager = Mockito.mock(ChainBaseManager.class); + Mockito.when(chainBaseManager.getDynamicPropertiesStore()).thenReturn(dynamicPropertiesStore); + Mockito.when(dynamicPropertiesStore.getAllowTvmOsaka()).thenReturn(1L); + + Field field = PbftDataSyncHandler.class.getDeclaredField("chainBaseManager"); + field.setAccessible(true); + field.set(pbftDataSyncHandler, chainBaseManager); + + Param.getInstance().setAgreeNodeCount(1); + Method method = PbftDataSyncHandler.class.getDeclaredMethod("validPbftSign", + Protocol.PBFTMessage.Raw.class, java.util.List.class, java.util.List.class); + method.setAccessible(true); + + Protocol.PBFTMessage.Raw raw = Protocol.PBFTMessage.Raw.newBuilder() + .setViewN(1) + .setEpoch(0) + .setDataType(Protocol.PBFTMessage.DataType.BLOCK) + .setMsgType(Protocol.PBFTMessage.MsgType.COMMIT) + .setData(ByteString.copyFromUtf8("block")) + .build(); + + boolean valid = (boolean) method.invoke(pbftDataSyncHandler, raw, + Collections.singletonList(ByteString.copyFrom(new byte[69])), + Collections.singletonList(ByteString.copyFrom(new byte[21]))); + Assert.assertFalse(valid); + } + + @Test + public void testValidPbftSignDedupesByAddress() throws Exception { + PbftDataSyncHandler pbftDataSyncHandler = new PbftDataSyncHandler(); + DynamicPropertiesStore dynamicPropertiesStore = Mockito.mock(DynamicPropertiesStore.class); + ChainBaseManager chainBaseManager = Mockito.mock(ChainBaseManager.class); + Mockito.when(chainBaseManager.getDynamicPropertiesStore()).thenReturn(dynamicPropertiesStore); + Mockito.when(dynamicPropertiesStore.getAllowTvmOsaka()).thenReturn(1L); + + Field field = PbftDataSyncHandler.class.getDeclaredField("chainBaseManager"); + field.setAccessible(true); + field.set(pbftDataSyncHandler, chainBaseManager); + + Param.getInstance().setAgreeNodeCount(2); + Method method = PbftDataSyncHandler.class.getDeclaredMethod("validPbftSign", + Protocol.PBFTMessage.Raw.class, java.util.List.class, java.util.List.class); + method.setAccessible(true); + + Protocol.PBFTMessage.Raw raw = Protocol.PBFTMessage.Raw.newBuilder() + .setViewN(1) + .setEpoch(0) + .setDataType(Protocol.PBFTMessage.DataType.BLOCK) + .setMsgType(Protocol.PBFTMessage.MsgType.COMMIT) + .setData(ByteString.copyFromUtf8("block")) + .build(); + + SignInterface signer = SignUtils.fromPrivate( + Hex.decode(PublicMethod.getRandomPrivateKey()), true); + byte[] sig65 = signer.Base64toBytes( + signer.signHash(Sha256Hash.hash(true, raw.toByteArray()))); + List paddedSigs = Arrays.asList( + ByteString.copyFrom(sig65), + ByteString.copyFrom(Arrays.copyOf(sig65, 66)), + ByteString.copyFrom(Arrays.copyOf(sig65, 67)), + ByteString.copyFrom(Arrays.copyOf(sig65, 68))); + List currentSrList = Collections.singletonList( + ByteString.copyFrom(signer.getAddress())); + + boolean valid = (boolean) method.invoke(pbftDataSyncHandler, raw, + paddedSigs, currentSrList); + Assert.assertFalse("trailing-byte padded duplicates must not inflate quorum", valid); + } } diff --git a/framework/src/test/java/org/tron/core/net/messagehandler/PbftMsgHandlerTest.java b/framework/src/test/java/org/tron/core/net/messagehandler/PbftMsgHandlerTest.java index 65a8f615bfe..16d703b99eb 100644 --- a/framework/src/test/java/org/tron/core/net/messagehandler/PbftMsgHandlerTest.java +++ b/framework/src/test/java/org/tron/core/net/messagehandler/PbftMsgHandlerTest.java @@ -5,6 +5,8 @@ import com.google.protobuf.ByteString; import java.io.File; import java.net.InetSocketAddress; +import java.security.SignatureException; +import java.util.Arrays; import org.bouncycastle.util.encoders.Hex; import org.junit.After; import org.junit.AfterClass; @@ -120,4 +122,52 @@ public void testPbft() throws Exception { Assert.assertEquals(1, PeerManager.getPeers().size()); } + + @Test + public void testPbftPaddedSigOsakaRejected() throws Exception { + 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(context, c1); + + peer = PeerManager.getPeers().get(0); + BlockCapsule blockCapsule = new BlockCapsule(1, Sha256Hash.ZERO_HASH, + System.currentTimeMillis(), ByteString.EMPTY); + Protocol.PBFTMessage.Raw raw = Protocol.PBFTMessage.Raw.newBuilder() + .setViewN(blockCapsule.getNum()) + .setEpoch(0) + .setDataType(Protocol.PBFTMessage.DataType.BLOCK) + .setMsgType(Protocol.PBFTMessage.MsgType.PREPREPARE) + .setData(blockCapsule.getBlockId().getByteString()) + .build(); + SignInterface sign = SignUtils.fromPrivate(Hex.decode(PublicMethod.getRandomPrivateKey()), + true); + byte[] sig = sign.Base64toBytes(sign.signHash(Sha256Hash.hash(true, raw.toByteArray()))); + byte[] paddedSig = Arrays.copyOf(sig, 69); + Protocol.PBFTMessage message = Protocol.PBFTMessage.newBuilder() + .setRawData(raw) + .setSignature(ByteString.copyFrom(paddedSig)) + .build(); + + PbftMessage pbftMessage = new PbftMessage(); + pbftMessage.setType(MessageTypes.PBFT_MSG.asByte()); + pbftMessage.setPbftMessage(message); + pbftMessage.setData(message.toByteArray()); + pbftMessage.setSwitch(blockCapsule.isSwitch()); + Param.getInstance().setPbftInterface(context.getBean(PbftBaseImpl.class)); + peer.setNeedSyncFromPeer(false); + + DynamicPropertiesStore dynamicPropertiesStore = context.getBean(DynamicPropertiesStore.class); + dynamicPropertiesStore.saveAllowPBFT(1); + dynamicPropertiesStore.saveAllowTvmOsaka(1); + try { + context.getBean(PbftMsgHandler.class).processMessage(peer, pbftMessage); + Assert.fail("Padded PBFT signature must be rejected post-Osaka"); + } catch (SignatureException e) { + Assert.assertTrue(e.getMessage().contains("PBFT signature size is 69")); + } finally { + dynamicPropertiesStore.saveAllowTvmOsaka(0); + } + } } 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..3d33bfb7e0e 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 @@ -10,6 +10,7 @@ import java.lang.reflect.Method; import java.net.InetSocketAddress; import java.util.ArrayList; +import java.util.Arrays; import java.util.Comparator; import java.util.List; import java.util.Set; @@ -226,6 +227,66 @@ private void testCheckHelloMessage() { } } + @Test + public void testCheckHelloMessagePaddedSigOsakaRejected() { + initWitness(); + String key = "0154435f065a57fec6af1e12eaa2fa600030639448d7809f4c65bdcf8baed7e5"; + ByteString address = getFromHexString("418A8D690BF36806C36A7DAE3AF796643C1AA9CC01"); + 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()); + byte[] sig = cryptoEngine.Base64toBytes(cryptoEngine.signHash(Sha256Hash.of(CommonParameter + .getInstance().isECKeyCryptoEngine(), ByteArray.fromLong(helloMessage + .getTimestamp())).getBytes())); + byte[] paddedSig = Arrays.copyOf(sig, 69); + helloMessage.setHelloMessage(helloMessage.getHelloMessage().toBuilder() + .setAddress(address) + .setSignature(ByteString.copyFrom(paddedSig)) + .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"); + PeerConnection peer1 = PeerManager.add(ctx, c1); + assert peer1 != null; + peer1.setAddress(address); + 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); + + chainBaseManager.getDynamicPropertiesStore().saveAllowTvmOsaka(1); + Assert.assertFalse(service.checkHelloMessage(helloMessage, c1)); + } catch (Exception e) { + logger.info("", e); + assert false; + } finally { + chainBaseManager.getDynamicPropertiesStore().saveAllowTvmOsaka(0); + } + } + @Test public void testNullWitnessAddress() { try { 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 7143cef43e2..ba10ed77fda 100755 --- a/framework/src/test/java/org/tron/core/zksnark/ShieldedReceiveTest.java +++ b/framework/src/test/java/org/tron/core/zksnark/ShieldedReceiveTest.java @@ -333,7 +333,8 @@ public void testBroadcastBeforeAllowZksnark() //Add public address sign transactionCap = TransactionUtils.addTransactionSign(transactionCap.getInstance(), - ADDRESS_ONE_PRIVATE_KEY, chainBaseManager.getAccountStore()); + ADDRESS_ONE_PRIVATE_KEY, chainBaseManager.getAccountStore(), + chainBaseManager.getDynamicPropertiesStore()); try { dbManager.pushTransaction(transactionCap); } catch (Exception e) {