Skip to content

Commit 2c50400

Browse files
authored
fix(security): cover consumed permission-change tx in getVerifyTxs (#6796)
Initialize multiAddresses from ownerAddressSet so that getVerifyTxs still forces re-verification of an owner's later txs even after the owner's permission-change tx has been packed and is no longer present in pendingTransactions. Main changes: - Manager.getVerifyTxs: seed multiAddresses with ownerAddressSet to cover the case where a permission-change tx has been consumed but ownerAddressSet still retains the owner (kept alive by in-flight txs in pushTransactionQueue / rePushTransactions via filterOwnerAddress). - ManagerTest: add getVerifyTxsSkipsBlockWhenPermissionTxAlreadyConsumed reproducing the bypass — pending contains only B (transfer, old sig, isVerified=true), ownerAddressSet contains the owner, block contains only B without the permission-change tx. Assertion checks B is placed in the re-verify list instead of being short-circuited via setVerified.
1 parent 78bc75d commit 2c50400

2 files changed

Lines changed: 43 additions & 1 deletion

File tree

framework/src/main/java/org/tron/core/db/Manager.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1235,7 +1235,7 @@ public List<TransactionCapsule> getVerifyTxs(BlockCapsule block) {
12351235

12361236
List<TransactionCapsule> txs = new ArrayList<>();
12371237
Map<String, TransactionCapsule> txMap = new HashMap<>();
1238-
Set<String> multiAddresses = new HashSet<>();
1238+
Set<String> multiAddresses = new HashSet<>(ownerAddressSet);
12391239

12401240
pendingTransactions.forEach(capsule -> {
12411241
String txId = Hex.toHexString(capsule.getTransactionId().getBytes());

framework/src/test/java/org/tron/core/db/ManagerTest.java

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import java.nio.charset.StandardCharsets;
2424
import java.util.ArrayList;
2525
import java.util.Arrays;
26+
import java.util.HashSet;
2627
import java.util.List;
2728
import java.util.Map;
2829
import java.util.Set;
@@ -876,6 +877,47 @@ public void getVerifyTxsTest() {
876877
Assert.assertEquals(txs.size(), 1);
877878
}
878879

880+
@Test
881+
public void getVerifyTxsSkipsBlockWhenPermissionTxAlreadyConsumed() throws Exception {
882+
// Scenario: a permission-change tx (A) for owner X has been processed and consumed,
883+
// so it is no longer in pendingTransactions but ownerAddressSet still contains X.
884+
// A later transfer tx (B) from X with the old signature enters pending with
885+
// isVerified=true. A malicious SR produces a block containing only B (no A).
886+
// getVerifyTxs must place B into the re-verify list rather than calling
887+
// setVerified(true) just because B matches the pending entry.
888+
TransferContract bContract = TransferContract.newBuilder()
889+
.setOwnerAddress(ByteString.copyFrom("f1".getBytes()))
890+
.setAmount(7).build();
891+
TransactionCapsule bTx = new TransactionCapsule(bContract, ContractType.TransferContract);
892+
String hexOwner = ByteArray.toHexString("f1".getBytes());
893+
894+
dbManager.getPendingTransactions().clear();
895+
dbManager.getPendingTransactions().add(bTx);
896+
897+
Field field = Manager.class.getDeclaredField("ownerAddressSet");
898+
field.setAccessible(true);
899+
@SuppressWarnings("unchecked")
900+
Set<String> ownerAddressSet = (Set<String>) field.get(dbManager);
901+
Set<String> backup = new HashSet<>(ownerAddressSet);
902+
ownerAddressSet.clear();
903+
ownerAddressSet.add(hexOwner);
904+
905+
try {
906+
List<Transaction> blockTxs = new ArrayList<>();
907+
blockTxs.add(bTx.getInstance());
908+
BlockCapsule capsule = new BlockCapsule(0, ByteString.EMPTY, 0, blockTxs);
909+
910+
List<TransactionCapsule> txs = dbManager.getVerifyTxs(capsule);
911+
912+
Assert.assertEquals(1, txs.size());
913+
Assert.assertEquals(bTx.getTransactionId(), txs.get(0).getTransactionId());
914+
} finally {
915+
ownerAddressSet.clear();
916+
ownerAddressSet.addAll(backup);
917+
dbManager.getPendingTransactions().clear();
918+
}
919+
}
920+
879921
@Test
880922
public void doNotSwitch()
881923
throws ValidateSignatureException, ContractValidateException, ContractExeException,

0 commit comments

Comments
 (0)