Skip to content

Commit 41ab356

Browse files
committed
Add Shanghai to the combined protocol schedule test, update IBFT message validator creation
Signed-off-by: Matthew Whitehead <matthew1001@gmail.com>
1 parent 94851a5 commit 41ab356

File tree

10 files changed

+95
-77
lines changed

10 files changed

+95
-77
lines changed

consensus/common/src/main/java/org/hyperledger/besu/consensus/common/CombinedProtocolScheduleFactory.java

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,14 +53,22 @@ public BftProtocolSchedule create(
5353
protocolSchedule.getScheduledProtocolSpecs().stream()
5454
.filter(protocolSpecMatchesConsensusBlockRange(spec.getBlock(), endBlock))
5555
.forEach(
56-
s ->
57-
combinedProtocolSchedule.putBlockNumberMilestone(s.fork().milestone(), s.spec()));
56+
s -> {
57+
if (s instanceof ScheduledProtocolSpec.TimestampProtocolSpec) {
58+
combinedProtocolSchedule.putTimestampMilestone(s.fork().milestone(), s.spec());
59+
} else if (s instanceof ScheduledProtocolSpec.BlockNumberProtocolSpec) {
60+
combinedProtocolSchedule.putBlockNumberMilestone(s.fork().milestone(), s.spec());
61+
} else {
62+
throw new IllegalStateException(
63+
"Unexpected milestone: " + s + " for milestone: " + s.fork().milestone());
64+
}
65+
});
5866

5967
// When moving to a new consensus mechanism we want to use the last milestone but created by
6068
// our consensus mechanism's BesuControllerBuilder so any additional rules are applied
6169
if (spec.getBlock() > 0) {
6270
combinedProtocolSchedule.putBlockNumberMilestone(
63-
spec.getBlock(), protocolSchedule.getByBlockNumber(spec.getBlock()));
71+
spec.getBlock(), protocolSchedule.getByBlockNumberAndTimestamp(spec.getBlock(), 0L));
6472
}
6573
}
6674
return combinedProtocolSchedule;

consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/BftProtocolSchedule.java

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -38,29 +38,6 @@ public BftProtocolSchedule(final DefaultProtocolSchedule protocolSchedule) {
3838
super(protocolSchedule);
3939
}
4040

41-
/**
42-
* Look up ProtocolSpec by block number
43-
*
44-
* @param number block number
45-
* @return the protocol spec for that block number
46-
*/
47-
public ProtocolSpec getByBlockNumber(final long number) {
48-
checkArgument(number >= 0, "number must be non-negative");
49-
checkArgument(
50-
!protocolSpecs.isEmpty(), "At least 1 milestone must be provided to the protocol schedule");
51-
checkArgument(
52-
protocolSpecs.last().fork().milestone() == 0,
53-
"There must be a milestone starting from block 0");
54-
// protocolSpecs is sorted in descending block order, so the first one we find that's lower than
55-
// the requested level will be the most appropriate spec
56-
for (final ScheduledProtocolSpec s : protocolSpecs) {
57-
if (number >= s.fork().milestone()) {
58-
return s.spec();
59-
}
60-
}
61-
return null;
62-
}
63-
6441
/**
6542
* Look up ProtocolSpec by block number and timestamp
6643
*

consensus/common/src/test/java/org/hyperledger/besu/consensus/common/CombinedProtocolScheduleFactoryTest.java

Lines changed: 52 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -60,18 +60,20 @@ public void createsCombinedProtocolScheduleWithMilestonesFromSingleProtocolSched
6060
final BftProtocolSchedule combinedProtocolSchedule =
6161
combinedProtocolScheduleFactory.create(consensusSchedule, Optional.of(BigInteger.TEN));
6262

63-
assertThat(combinedProtocolSchedule.getByBlockNumber(0L).getName()).isEqualTo("Frontier");
64-
assertThat(combinedProtocolSchedule.getByBlockNumber(0L))
65-
.isSameAs(protocolSchedule.getByBlockNumber(0L));
63+
assertThat(combinedProtocolSchedule.getByBlockNumberAndTimestamp(0L, 0L).getName())
64+
.isEqualTo("Frontier");
65+
assertThat(combinedProtocolSchedule.getByBlockNumberAndTimestamp(0L, 0L))
66+
.isSameAs(protocolSchedule.getByBlockNumberAndTimestamp(0L, 0L));
6667

67-
assertThat(combinedProtocolSchedule.getByBlockNumber(5L).getName()).isEqualTo("Homestead");
68-
assertThat(combinedProtocolSchedule.getByBlockNumber(5L))
69-
.isSameAs(protocolSchedule.getByBlockNumber(5L));
68+
assertThat(combinedProtocolSchedule.getByBlockNumberAndTimestamp(5L, 0L).getName())
69+
.isEqualTo("Homestead");
70+
assertThat(combinedProtocolSchedule.getByBlockNumberAndTimestamp(5L, 0L))
71+
.isSameAs(protocolSchedule.getByBlockNumberAndTimestamp(5L, 0L));
7072

71-
assertThat(combinedProtocolSchedule.getByBlockNumber(10L).getName())
73+
assertThat(combinedProtocolSchedule.getByBlockNumberAndTimestamp(10L, 0L).getName())
7274
.isEqualTo("Constantinople");
73-
assertThat(combinedProtocolSchedule.getByBlockNumber(10L))
74-
.isSameAs(protocolSchedule.getByBlockNumber(10L));
75+
assertThat(combinedProtocolSchedule.getByBlockNumberAndTimestamp(10L, 0L))
76+
.isSameAs(protocolSchedule.getByBlockNumberAndTimestamp(10L, 0L));
7577

7678
assertThat(
7779
new MilestoneStreamingProtocolSchedule(combinedProtocolSchedule)
@@ -88,63 +90,78 @@ public void createsCombinedProtocolScheduleWithMilestonesFromMultipleSchedules()
8890
genesisConfigOptions.byzantiumBlock(105L);
8991
genesisConfigOptions.berlinBlock(110L);
9092
genesisConfigOptions.londonBlock(220L);
93+
genesisConfigOptions.shanghaiTime(1000000050L);
9194
genesisConfigOptions.chainId(BigInteger.TEN);
9295

9396
final BftProtocolSchedule protocolSchedule1 = createProtocolSchedule(genesisConfigOptions);
9497
final BftProtocolSchedule protocolSchedule2 = createProtocolSchedule(genesisConfigOptions);
9598
final BftProtocolSchedule protocolSchedule3 = createProtocolSchedule(genesisConfigOptions);
99+
final BftProtocolSchedule protocolSchedule4 = createProtocolSchedule(genesisConfigOptions);
96100

97101
final NavigableSet<ForkSpec<ProtocolSchedule>> consensusSchedule =
98102
new TreeSet<>(ForkSpec.COMPARATOR);
99103
consensusSchedule.add(new ForkSpec<>(0, protocolSchedule1));
100104
consensusSchedule.add(new ForkSpec<>(100L, protocolSchedule2));
101105
consensusSchedule.add(new ForkSpec<>(200L, protocolSchedule3));
106+
consensusSchedule.add(new ForkSpec<>(1000000000L, protocolSchedule4));
102107

103108
final BftProtocolSchedule combinedProtocolSchedule =
104109
combinedProtocolScheduleFactory.create(consensusSchedule, Optional.of(BigInteger.TEN));
105110

106111
// consensus schedule 1
107-
assertThat(combinedProtocolSchedule.getByBlockNumber(0L).getName()).isEqualTo("Frontier");
108-
assertThat(combinedProtocolSchedule.getByBlockNumber(0L))
109-
.isSameAs(protocolSchedule1.getByBlockNumber(0L));
110-
111-
assertThat(combinedProtocolSchedule.getByBlockNumber(5L).getName()).isEqualTo("Homestead");
112-
assertThat(combinedProtocolSchedule.getByBlockNumber(5L))
113-
.isSameAs(protocolSchedule1.getByBlockNumber(5L));
114-
assertThat(combinedProtocolSchedule.getByBlockNumber(10L).getName())
112+
assertThat(combinedProtocolSchedule.getByBlockNumberAndTimestamp(0L, 0L).getName())
113+
.isEqualTo("Frontier");
114+
assertThat(combinedProtocolSchedule.getByBlockNumberAndTimestamp(0L, 0L))
115+
.isSameAs(protocolSchedule1.getByBlockNumberAndTimestamp(0L, 0L));
116+
117+
assertThat(combinedProtocolSchedule.getByBlockNumberAndTimestamp(5L, 0L).getName())
118+
.isEqualTo("Homestead");
119+
assertThat(combinedProtocolSchedule.getByBlockNumberAndTimestamp(5L, 0L))
120+
.isSameAs(protocolSchedule1.getByBlockNumberAndTimestamp(5L, 0L));
121+
assertThat(combinedProtocolSchedule.getByBlockNumberAndTimestamp(10L, 0L).getName())
115122
.isEqualTo("Constantinople");
116-
assertThat(combinedProtocolSchedule.getByBlockNumber(10L))
117-
.isSameAs(protocolSchedule1.getByBlockNumber(10L));
123+
assertThat(combinedProtocolSchedule.getByBlockNumberAndTimestamp(10L, 0L))
124+
.isSameAs(protocolSchedule1.getByBlockNumberAndTimestamp(10L, 0L));
118125

119126
// consensus schedule 2 migration block
120-
assertThat(combinedProtocolSchedule.getByBlockNumber(100L).getName())
127+
assertThat(combinedProtocolSchedule.getByBlockNumberAndTimestamp(100L, 0L).getName())
121128
.isEqualTo("Constantinople");
122-
assertThat(combinedProtocolSchedule.getByBlockNumber(100L))
123-
.isSameAs(protocolSchedule2.getByBlockNumber(10L));
129+
assertThat(combinedProtocolSchedule.getByBlockNumberAndTimestamp(100L, 0L))
130+
.isSameAs(protocolSchedule2.getByBlockNumberAndTimestamp(10L, 0L));
124131

125132
// consensus schedule 2
126-
assertThat(combinedProtocolSchedule.getByBlockNumber(105L).getName()).isEqualTo("Byzantium");
127-
assertThat(combinedProtocolSchedule.getByBlockNumber(105L))
128-
.isSameAs(protocolSchedule2.getByBlockNumber(105L));
129-
assertThat(combinedProtocolSchedule.getByBlockNumber(110L).getName()).isEqualTo("Berlin");
130-
assertThat(combinedProtocolSchedule.getByBlockNumber(110L))
131-
.isSameAs(protocolSchedule2.getByBlockNumber(110L));
133+
assertThat(combinedProtocolSchedule.getByBlockNumberAndTimestamp(105L, 0L).getName())
134+
.isEqualTo("Byzantium");
135+
assertThat(combinedProtocolSchedule.getByBlockNumberAndTimestamp(105L, 0L))
136+
.isSameAs(protocolSchedule2.getByBlockNumberAndTimestamp(105L, 0L));
137+
assertThat(combinedProtocolSchedule.getByBlockNumberAndTimestamp(110L, 0L).getName())
138+
.isEqualTo("Berlin");
139+
assertThat(combinedProtocolSchedule.getByBlockNumberAndTimestamp(110L, 0L))
140+
.isSameAs(protocolSchedule2.getByBlockNumberAndTimestamp(110L, 0L));
132141

133142
// consensus schedule 3 migration block
134-
assertThat(combinedProtocolSchedule.getByBlockNumber(200L).getName()).isEqualTo("Berlin");
135-
assertThat(combinedProtocolSchedule.getByBlockNumber(200L))
136-
.isSameAs(protocolSchedule3.getByBlockNumber(110L));
143+
assertThat(combinedProtocolSchedule.getByBlockNumberAndTimestamp(200L, 0L).getName())
144+
.isEqualTo("Berlin");
145+
assertThat(combinedProtocolSchedule.getByBlockNumberAndTimestamp(200L, 0L))
146+
.isSameAs(protocolSchedule3.getByBlockNumberAndTimestamp(110L, 0L));
137147

138148
// consensus schedule 3
139-
assertThat(combinedProtocolSchedule.getByBlockNumber(220L).getName()).isEqualTo("London");
140-
assertThat(combinedProtocolSchedule.getByBlockNumber(220L))
141-
.isSameAs(protocolSchedule3.getByBlockNumber(220L));
149+
assertThat(combinedProtocolSchedule.getByBlockNumberAndTimestamp(220L, 0L).getName())
150+
.isEqualTo("London");
151+
assertThat(combinedProtocolSchedule.getByBlockNumberAndTimestamp(220L, 0L))
152+
.isSameAs(protocolSchedule3.getByBlockNumberAndTimestamp(220L, 0L));
153+
154+
// consensus schedule 4
155+
assertThat(combinedProtocolSchedule.getByBlockNumberAndTimestamp(0L, 1000000050L).getName())
156+
.isEqualTo("Shanghai");
157+
assertThat(combinedProtocolSchedule.getByBlockNumberAndTimestamp(220L, 1000000050L))
158+
.isSameAs(protocolSchedule4.getByBlockNumberAndTimestamp(220L, 1000000050L));
142159

143160
assertThat(
144161
new MilestoneStreamingProtocolSchedule(combinedProtocolSchedule)
145162
.streamMilestoneBlocks()
146163
.collect(Collectors.toList()))
147-
.isEqualTo(List.of(0L, 5L, 10L, 100L, 105L, 110L, 200L, 220L));
164+
.isEqualTo(List.of(0L, 5L, 10L, 100L, 105L, 110L, 200L, 220L, 1000000000L, 1000000050L));
148165
}
149166

150167
private BftProtocolSchedule createProtocolSchedule(

consensus/ibft/src/main/java/org/hyperledger/besu/consensus/ibft/validation/MessageValidator.java

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
import org.hyperledger.besu.consensus.common.bft.BftBlockInterface;
1818
import org.hyperledger.besu.consensus.common.bft.BftContext;
19+
import org.hyperledger.besu.consensus.common.bft.BftProtocolSchedule;
1920
import org.hyperledger.besu.consensus.common.bft.ConsensusRoundIdentifier;
2021
import org.hyperledger.besu.consensus.ibft.messagewrappers.Commit;
2122
import org.hyperledger.besu.consensus.ibft.messagewrappers.Prepare;
@@ -25,6 +26,7 @@
2526
import org.hyperledger.besu.ethereum.ProtocolContext;
2627
import org.hyperledger.besu.ethereum.core.Block;
2728
import org.hyperledger.besu.ethereum.mainnet.HeaderValidationMode;
29+
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
2830

2931
import java.util.Optional;
3032

@@ -38,7 +40,7 @@ public class MessageValidator {
3840

3941
private final SignedDataValidator signedDataValidator;
4042
private final ProposalBlockConsistencyValidator proposalConsistencyValidator;
41-
private final BlockValidator blockValidator;
43+
private final ProtocolSchedule protocolSchedule;
4244
private final ProtocolContext protocolContext;
4345
private final RoundChangeCertificateValidator roundChangeCertificateValidator;
4446

@@ -47,19 +49,19 @@ public class MessageValidator {
4749
*
4850
* @param signedDataValidator the signed data validator
4951
* @param proposalConsistencyValidator the proposal consistency validator
50-
* @param blockValidator the block validator
52+
* @param protocolSchedule the proposal schedule
5153
* @param protocolContext the protocol context
5254
* @param roundChangeCertificateValidator the round change certificate validator
5355
*/
5456
public MessageValidator(
5557
final SignedDataValidator signedDataValidator,
5658
final ProposalBlockConsistencyValidator proposalConsistencyValidator,
57-
final BlockValidator blockValidator,
5859
final ProtocolContext protocolContext,
60+
final ProtocolSchedule protocolSchedule,
5961
final RoundChangeCertificateValidator roundChangeCertificateValidator) {
6062
this.signedDataValidator = signedDataValidator;
6163
this.proposalConsistencyValidator = proposalConsistencyValidator;
62-
this.blockValidator = blockValidator;
64+
this.protocolSchedule = protocolSchedule;
6365
this.protocolContext = protocolContext;
6466
this.roundChangeCertificateValidator = roundChangeCertificateValidator;
6567
}
@@ -93,6 +95,13 @@ public boolean validateProposal(final Proposal msg) {
9395
}
9496

9597
private boolean validateBlock(final Block block) {
98+
99+
final BlockValidator blockValidator =
100+
((BftProtocolSchedule) protocolSchedule)
101+
.getByBlockNumberAndTimestamp(
102+
block.getHeader().getNumber(), block.getHeader().getTimestamp())
103+
.getBlockValidator();
104+
96105
final var validationResult =
97106
blockValidator.validateAndProcessBlock(
98107
protocolContext, block, HeaderValidationMode.LIGHT, HeaderValidationMode.FULL);

consensus/ibft/src/main/java/org/hyperledger/besu/consensus/ibft/validation/MessageValidatorFactory.java

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
import org.hyperledger.besu.consensus.common.bft.ConsensusRoundIdentifier;
2323
import org.hyperledger.besu.consensus.common.bft.blockcreation.ProposerSelector;
2424
import org.hyperledger.besu.datatypes.Address;
25-
import org.hyperledger.besu.ethereum.BlockValidator;
2625
import org.hyperledger.besu.ethereum.ProtocolContext;
2726
import org.hyperledger.besu.ethereum.core.BlockHeader;
2827

@@ -80,8 +79,6 @@ private SignedDataValidator createSignedDataValidator(
8079
*/
8180
public MessageValidator createMessageValidator(
8281
final ConsensusRoundIdentifier roundIdentifier, final BlockHeader parentHeader) {
83-
final BlockValidator blockValidator =
84-
protocolSchedule.getByBlockNumber(roundIdentifier.getSequenceNumber()).getBlockValidator();
8582
final Collection<Address> validators = getValidatorsAfterBlock(parentHeader);
8683

8784
final BftBlockInterface bftBlockInterface =
@@ -90,8 +87,8 @@ public MessageValidator createMessageValidator(
9087
return new MessageValidator(
9188
createSignedDataValidator(roundIdentifier, parentHeader),
9289
new ProposalBlockConsistencyValidator(),
93-
blockValidator,
9490
protocolContext,
91+
protocolSchedule,
9592
new RoundChangeCertificateValidator(
9693
validators,
9794
(ri) -> createSignedDataValidator(ri, parentHeader),

consensus/ibft/src/test/java/org/hyperledger/besu/consensus/ibft/IbftProtocolScheduleTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ private boolean validateHeader(
110110
final BlockHeader blockHeader,
111111
final int block) {
112112
return schedule
113-
.getByBlockNumber(block)
113+
.getByBlockNumberAndTimestamp(block, blockHeader.getTimestamp())
114114
.getBlockHeaderValidator()
115115
.validateHeader(
116116
blockHeader, parentHeader, protocolContext(validators), HeaderValidationMode.LIGHT);

consensus/ibft/src/test/java/org/hyperledger/besu/consensus/ibft/validation/MessageValidatorTest.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import static java.util.Collections.emptyList;
1818
import static org.assertj.core.api.Assertions.assertThat;
1919
import static org.mockito.ArgumentMatchers.any;
20+
import static org.mockito.ArgumentMatchers.anyLong;
2021
import static org.mockito.ArgumentMatchers.eq;
2122
import static org.mockito.Mockito.lenient;
2223
import static org.mockito.Mockito.mock;
@@ -26,6 +27,7 @@
2627
import static org.mockito.Mockito.when;
2728

2829
import org.hyperledger.besu.consensus.common.bft.BftContext;
30+
import org.hyperledger.besu.consensus.common.bft.BftProtocolSchedule;
2931
import org.hyperledger.besu.consensus.common.bft.ConsensusRoundIdentifier;
3032
import org.hyperledger.besu.consensus.common.bft.ProposedBlockHelpers;
3133
import org.hyperledger.besu.consensus.ibft.messagewrappers.Commit;
@@ -42,6 +44,7 @@
4244
import org.hyperledger.besu.ethereum.chain.MutableBlockchain;
4345
import org.hyperledger.besu.ethereum.core.AddressHelpers;
4446
import org.hyperledger.besu.ethereum.core.Block;
47+
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
4548
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
4649

4750
import java.util.List;
@@ -66,6 +69,8 @@ public class MessageValidatorTest {
6669
private final ProposalBlockConsistencyValidator proposalBlockConsistencyValidator =
6770
mock(ProposalBlockConsistencyValidator.class);
6871

72+
@Mock private BftProtocolSchedule protocolSchedule;
73+
@Mock private ProtocolSpec protocolSpec;
6974
@Mock private BlockValidator blockValidator;
7075
private ProtocolContext protocolContext;
7176
private final RoundChangeCertificateValidator roundChangeCertificateValidator =
@@ -101,6 +106,11 @@ public void setup() {
101106
mockBftCtx,
102107
Optional.empty());
103108

109+
lenient()
110+
.when(protocolSchedule.getByBlockNumberAndTimestamp(anyLong(), anyLong()))
111+
.thenReturn(protocolSpec);
112+
113+
lenient().when(protocolSpec.getBlockValidator()).thenReturn(blockValidator);
104114
when(blockValidator.validateAndProcessBlock(any(), any(), any(), any()))
105115
.thenReturn(new BlockProcessingResult(Optional.empty()));
106116

@@ -115,8 +125,8 @@ public void setup() {
115125
new MessageValidator(
116126
signedDataValidator,
117127
proposalBlockConsistencyValidator,
118-
blockValidator,
119128
protocolContext,
129+
protocolSchedule,
120130
roundChangeCertificateValidator);
121131
}
122132

consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/validation/ProposalPayloadValidator.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,7 @@ public class ProposalPayloadValidator {
5555
*
5656
* @param expectedProposer the expected proposer
5757
* @param targetRound the target round
58-
* @param blockValidator the optional block validator (not all proposal validation requires block
59-
* validation)
58+
* @param blockValidator the block validator
6059
* @param protocolContext the protocol context
6160
* @param bftExtraDataCodec the bft extra data codec
6261
*/

consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/validation/ProposalValidator.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
*/
1515
package org.hyperledger.besu.consensus.qbft.validation;
1616

17+
import static com.google.common.base.Preconditions.checkState;
1718
import static org.hyperledger.besu.consensus.common.bft.validation.ValidationHelpers.hasDuplicateAuthors;
1819
import static org.hyperledger.besu.consensus.common.bft.validation.ValidationHelpers.hasSufficientEntries;
1920

@@ -95,9 +96,9 @@ public ProposalValidator(
9596
*/
9697
public boolean validate(final Proposal msg) {
9798

98-
if (!(protocolSchedule instanceof BftProtocolSchedule)) {
99-
throw new RuntimeException("Wrong class type for protocol schedule");
100-
}
99+
checkState(
100+
protocolSchedule instanceof BftProtocolSchedule,
101+
"Wrong class type for protocol schedule, requires BftProtocolSchedule");
101102

102103
final BlockValidator blockValidator =
103104
((BftProtocolSchedule) protocolSchedule)

consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/QbftProtocolScheduleTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ private boolean validateHeader(
144144
final BlockHeader blockHeader,
145145
final int block) {
146146
return schedule
147-
.getByBlockNumber(block)
147+
.getByBlockNumberAndTimestamp(block, blockHeader.getTimestamp())
148148
.getBlockHeaderValidator()
149149
.validateHeader(
150150
blockHeader, parentHeader, protocolContext(validators), HeaderValidationMode.LIGHT);

0 commit comments

Comments
 (0)