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 667bfce034..3de0260b5c 100644 --- a/framework/src/main/java/org/tron/core/db/Manager.java +++ b/framework/src/main/java/org/tron/core/db/Manager.java @@ -1235,7 +1235,7 @@ public List getVerifyTxs(BlockCapsule block) { List txs = new ArrayList<>(); Map txMap = new HashMap<>(); - Set multiAddresses = new HashSet<>(); + Set multiAddresses = new HashSet<>(ownerAddressSet); pendingTransactions.forEach(capsule -> { String txId = Hex.toHexString(capsule.getTransactionId().getBytes()); 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 87b4fcfdc7..717d6c4cf6 100755 --- a/framework/src/test/java/org/tron/core/db/ManagerTest.java +++ b/framework/src/test/java/org/tron/core/db/ManagerTest.java @@ -23,6 +23,7 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -876,6 +877,47 @@ public void getVerifyTxsTest() { Assert.assertEquals(txs.size(), 1); } + @Test + public void getVerifyTxsSkipsBlockWhenPermissionTxAlreadyConsumed() throws Exception { + // Scenario: a permission-change tx (A) for owner X has been processed and consumed, + // so it is no longer in pendingTransactions but ownerAddressSet still contains X. + // A later transfer tx (B) from X with the old signature enters pending with + // isVerified=true. A malicious SR produces a block containing only B (no A). + // getVerifyTxs must place B into the re-verify list rather than calling + // setVerified(true) just because B matches the pending entry. + TransferContract bContract = TransferContract.newBuilder() + .setOwnerAddress(ByteString.copyFrom("f1".getBytes())) + .setAmount(7).build(); + TransactionCapsule bTx = new TransactionCapsule(bContract, ContractType.TransferContract); + String hexOwner = ByteArray.toHexString("f1".getBytes()); + + dbManager.getPendingTransactions().clear(); + dbManager.getPendingTransactions().add(bTx); + + Field field = Manager.class.getDeclaredField("ownerAddressSet"); + field.setAccessible(true); + @SuppressWarnings("unchecked") + Set ownerAddressSet = (Set) field.get(dbManager); + Set backup = new HashSet<>(ownerAddressSet); + ownerAddressSet.clear(); + ownerAddressSet.add(hexOwner); + + try { + List blockTxs = new ArrayList<>(); + blockTxs.add(bTx.getInstance()); + BlockCapsule capsule = new BlockCapsule(0, ByteString.EMPTY, 0, blockTxs); + + List txs = dbManager.getVerifyTxs(capsule); + + Assert.assertEquals(1, txs.size()); + Assert.assertEquals(bTx.getTransactionId(), txs.get(0).getTransactionId()); + } finally { + ownerAddressSet.clear(); + ownerAddressSet.addAll(backup); + dbManager.getPendingTransactions().clear(); + } + } + @Test public void doNotSwitch() throws ValidateSignatureException, ContractValidateException, ContractExeException,