Skip to content

Commit d6065a4

Browse files
committed
feat(log): slow signature verifications above 50ms
1 parent 8f7f16c commit d6065a4

2 files changed

Lines changed: 84 additions & 0 deletions

File tree

chainbase/src/main/java/org/tron/core/capsule/TransactionCapsule.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import java.util.List;
3636
import java.util.concurrent.ExecutorService;
3737
import java.util.concurrent.Future;
38+
import java.util.concurrent.TimeUnit;
3839
import java.util.concurrent.atomic.AtomicInteger;
3940
import lombok.Getter;
4041
import lombok.Setter;
@@ -94,6 +95,8 @@ public class TransactionCapsule implements ProtoCapsule<Transaction> {
9495
.newFixedThreadPool(esName, CommonParameter.getInstance()
9596
.getValidContractProtoThreadNum());
9697
private static final String OWNER_ADDRESS = "ownerAddress_";
98+
// 2-6 ms in general, so we set 50 ms as the threshold for slow signature verification.
99+
private static final long SLOW_SIG_VERIFY_MS = 50;
97100

98101
private Transaction transaction;
99102
@Setter
@@ -648,6 +651,7 @@ public boolean validatePubSignature(AccountStore accountStore,
648651

649652
byte[] hash = getTransactionId().getBytes();
650653

654+
long startNs = System.nanoTime();
651655
try {
652656
if (!validateSignature(this.transaction, hash, accountStore, dynamicPropertiesStore)) {
653657
isVerified = false;
@@ -656,12 +660,27 @@ public boolean validatePubSignature(AccountStore accountStore,
656660
} catch (SignatureException | PermissionException | SignatureFormatException e) {
657661
isVerified = false;
658662
throw new ValidateSignatureException(e.getMessage());
663+
} finally {
664+
logSlowSigVerify(startNs);
659665
}
660666
isVerified = true;
661667
}
662668
return true;
663669
}
664670

671+
/**
672+
* INFO-logs when a single signature verification exceeds
673+
* {@link #SLOW_SIG_VERIFY_MS}. Package-private so it can be exercised from
674+
* tests without forcing a real slow crypto path.
675+
*/
676+
void logSlowSigVerify(long startNs) {
677+
long costMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs);
678+
if (costMs > SLOW_SIG_VERIFY_MS) {
679+
logger.info("slow verify: txId={}, sigCount={}, cost={} ms",
680+
getTransactionId(), this.transaction.getSignatureCount(), costMs);
681+
}
682+
}
683+
665684
/**
666685
* validate signature
667686
*/

framework/src/test/java/org/tron/core/capsule/TransactionCapsuleTest.java

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,20 @@
44
import static org.tron.protos.Protocol.Transaction.Result.contractResult.PRECOMPILED_CONTRACT;
55
import static org.tron.protos.Protocol.Transaction.Result.contractResult.SUCCESS;
66

7+
import ch.qos.logback.classic.Level;
8+
import ch.qos.logback.classic.Logger;
9+
import ch.qos.logback.classic.spi.ILoggingEvent;
10+
import ch.qos.logback.core.read.ListAppender;
711
import com.google.protobuf.ByteString;
12+
import java.util.List;
13+
import java.util.concurrent.TimeUnit;
14+
import java.util.stream.Collectors;
815
import lombok.extern.slf4j.Slf4j;
916
import org.junit.Assert;
1017
import org.junit.Before;
1118
import org.junit.BeforeClass;
1219
import org.junit.Test;
20+
import org.slf4j.LoggerFactory;
1321
import org.tron.common.BaseTest;
1422
import org.tron.common.TestConstants;
1523
import org.tron.common.utils.StringUtil;
@@ -69,4 +77,61 @@ public void testRemoveRedundantRet() {
6977
Assert.assertEquals(1, transactionCapsule.getInstance().getRetCount());
7078
Assert.assertEquals(SUCCESS, transactionCapsule.getInstance().getRet(0).getContractRet());
7179
}
80+
81+
@Test
82+
public void slowVerify() {
83+
Logger capsuleLogger = (Logger) LoggerFactory.getLogger("capsule");
84+
Level originalLevel = capsuleLogger.getLevel();
85+
capsuleLogger.setLevel(Level.INFO);
86+
ListAppender<ILoggingEvent> appender = new ListAppender<>();
87+
appender.start();
88+
capsuleLogger.addAppender(appender);
89+
try {
90+
TransactionCapsule cap = new TransactionCapsule(Transaction.newBuilder().build());
91+
long startNs = System.nanoTime() - TimeUnit.MILLISECONDS.toNanos(51);
92+
cap.logSlowSigVerify(startNs);
93+
94+
List<ILoggingEvent> infos = appender.list.stream()
95+
.filter(e -> e.getLevel() == Level.INFO)
96+
.collect(Collectors.toList());
97+
Assert.assertEquals("expected one INFO for a slow verify", 1, infos.size());
98+
String rendered = infos.get(0).getFormattedMessage();
99+
Assert.assertTrue("INFO should mention slow verify: " + rendered,
100+
rendered.contains("slow verify"));
101+
Assert.assertTrue("INFO should echo the txId: " + rendered,
102+
rendered.contains(cap.getTransactionId().toString()));
103+
Assert.assertTrue("INFO should include sigCount: " + rendered,
104+
rendered.contains("sigCount="));
105+
Assert.assertTrue("INFO should include cost in ms: " + rendered,
106+
rendered.contains("cost="));
107+
Assert.assertTrue("INFO should render ms suffix: " + rendered,
108+
rendered.contains(" ms"));
109+
} finally {
110+
appender.stop();
111+
capsuleLogger.detachAppender(appender);
112+
capsuleLogger.setLevel(originalLevel);
113+
}
114+
}
115+
116+
@Test
117+
public void fastVerify() {
118+
Logger capsuleLogger = (Logger) LoggerFactory.getLogger("capsule");
119+
Level originalLevel = capsuleLogger.getLevel();
120+
capsuleLogger.setLevel(Level.INFO);
121+
ListAppender<ILoggingEvent> appender = new ListAppender<>();
122+
appender.start();
123+
capsuleLogger.addAppender(appender);
124+
try {
125+
TransactionCapsule cap = new TransactionCapsule(Transaction.newBuilder().build());
126+
cap.logSlowSigVerify(System.nanoTime());
127+
long infoCount = appender.list.stream()
128+
.filter(e -> e.getLevel() == Level.INFO)
129+
.count();
130+
Assert.assertEquals("no INFO should fire below the threshold", 0, infoCount);
131+
} finally {
132+
appender.stop();
133+
capsuleLogger.detachAppender(appender);
134+
capsuleLogger.setLevel(originalLevel);
135+
}
136+
}
72137
}

0 commit comments

Comments
 (0)