Predict next bound offset (#691)

* predictLowerBound with offset

* remove method predictLowerBound without offset

* bump emerald submodule
This commit is contained in:
Andrey Bronin
2025-08-14 17:46:40 +03:00
committed by GitHub
parent 5ef7f8d414
commit 77b1d1fa28
12 changed files with 29 additions and 33 deletions

View File

@@ -168,7 +168,7 @@ abstract class DefaultUpstream(
// NOOP // NOOP
} }
override fun predictLowerBound(type: LowerBoundType): Long { override fun predictLowerBound(type: LowerBoundType, timeOffsetSeconds: Long): Long {
return 0 return 0
} }

View File

@@ -257,7 +257,7 @@ abstract class Multistream(
return getAll().any { it.isAvailable() } return getAll().any { it.isAvailable() }
} }
override fun predictLowerBound(type: LowerBoundType): Long { override fun predictLowerBound(type: LowerBoundType, timeOffsetSeconds: Long): Long {
return 0 return 0
} }

View File

@@ -108,6 +108,7 @@ class Selector {
LowerHeightMatcher( LowerHeightMatcher(
it.lowerHeightSelector.height, it.lowerHeightSelector.height,
it.lowerHeightSelector.lowerBoundType.fromProtoType(), it.lowerHeightSelector.lowerBoundType.fromProtoType(),
it.lowerHeightSelector.timeOffset,
) )
} else { } else {
empty empty
@@ -569,9 +570,10 @@ class Selector {
data class LowerHeightMatcher( data class LowerHeightMatcher(
private val lowerHeight: Long, private val lowerHeight: Long,
private val boundType: LowerBoundType, private val boundType: LowerBoundType,
private val timeOffsetSeconds: Long = 0,
) : Matcher() { ) : Matcher() {
override fun matchesWithCause(up: Upstream): MatchesResponse { override fun matchesWithCause(up: Upstream): MatchesResponse {
val predictedLowerBound = up.predictLowerBound(boundType) val predictedLowerBound = up.predictLowerBound(boundType, timeOffsetSeconds)
return if (lowerHeight >= predictedLowerBound && predictedLowerBound != 0L) { return if (lowerHeight >= predictedLowerBound && predictedLowerBound != 0L) {
Success Success
} else { } else {

View File

@@ -55,7 +55,7 @@ interface Upstream : Lifecycle {
fun addFinalization(finalization: FinalizationData, upstreamId: String) fun addFinalization(finalization: FinalizationData, upstreamId: String)
fun getUpstreamSettingsData(): UpstreamSettingsData? fun getUpstreamSettingsData(): UpstreamSettingsData?
fun updateLowerBound(lowerBound: Long, type: LowerBoundType) fun updateLowerBound(lowerBound: Long, type: LowerBoundType)
fun predictLowerBound(type: LowerBoundType): Long fun predictLowerBound(type: LowerBoundType, timeOffsetSeconds: Long): Long
fun getChain(): Chain fun getChain(): Chain

View File

@@ -437,8 +437,8 @@ open class GenericUpstream(
lowerBoundService.updateLowerBound(lowerBound, type) lowerBoundService.updateLowerBound(lowerBound, type)
} }
override fun predictLowerBound(type: LowerBoundType): Long { override fun predictLowerBound(type: LowerBoundType, timeOffsetSeconds: Long): Long {
return lowerBoundService.predictLowerBound(type) return lowerBoundService.predictLowerBound(type, timeOffsetSeconds)
} }
fun isValid(): Boolean = isUpstreamValid.get() fun isValid(): Boolean = isUpstreamValid.get()

View File

@@ -64,7 +64,7 @@ abstract class LowerBoundDetector(
lowerBoundSink.emitNext(LowerBoundData(lowerBound, type)) { _, res -> res == Sinks.EmitResult.FAIL_NON_SERIALIZED } lowerBoundSink.emitNext(LowerBoundData(lowerBound, type)) { _, res -> res == Sinks.EmitResult.FAIL_NON_SERIALIZED }
} }
fun predictLowerBound(type: LowerBoundType): Long { fun predictLowerBound(type: LowerBoundType, timeOffsetSeconds: Long): Long {
return lowerBounds.predictNextBound(type) return lowerBounds.predictNextBound(type, timeOffsetSeconds)
} }
} }

View File

@@ -33,10 +33,10 @@ abstract class LowerBoundService(
.forEach { it.updateLowerBound(lowerBound, type) } .forEach { it.updateLowerBound(lowerBound, type) }
} }
fun predictLowerBound(type: LowerBoundType): Long { fun predictLowerBound(type: LowerBoundType, timeOffsetSeconds: Long): Long {
return detectors return detectors
.firstOrNull { it.types().contains(type) } .firstOrNull { it.types().contains(type) }
?.predictLowerBound(type) ?.predictLowerBound(type, timeOffsetSeconds)
?: 0 ?: 0
} }

View File

@@ -67,16 +67,10 @@ class LowerBounds(
} }
} }
fun predictNextBound(type: LowerBoundType): Long { fun predictNextBound(type: LowerBoundType, timeOffsetSeconds: Long): Long {
val offset = if (chain == Chain.BSC__MAINNET && type == LowerBoundType.STATE) {
30
} else {
0
}.toLong()
val lowerBoundCoeffs = lowerBounds[type] ?: return 0 val lowerBoundCoeffs = lowerBounds[type] ?: return 0
val xTime = Instant.now().plus(offset, ChronoUnit.SECONDS).epochSecond val xTime = Instant.now().plus(timeOffsetSeconds, ChronoUnit.SECONDS).epochSecond
return (lowerBoundCoeffs.k.get() * xTime + lowerBoundCoeffs.b.get()).roundToLong() return (lowerBoundCoeffs.k.get() * xTime + lowerBoundCoeffs.b.get()).roundToLong()
} }

View File

@@ -225,7 +225,7 @@ class SelectorTest {
expected: MatchesResponse, expected: MatchesResponse,
) { ) {
val up = mock<Upstream> { val up = mock<Upstream> {
on { predictLowerBound(LowerBoundType.STATE) } doReturn predicted on { predictLowerBound(LowerBoundType.STATE, 0) } doReturn predicted
} }
val matcher = Selector.LowerHeightMatcher(lowerHeight, LowerBoundType.STATE) val matcher = Selector.LowerHeightMatcher(lowerHeight, LowerBoundType.STATE)

View File

@@ -15,15 +15,15 @@ class LowerBoundServiceTest {
@Test @Test
fun `predict lower bound`() { fun `predict lower bound`() {
val detector = mock<LowerBoundDetector> { val detector = mock<LowerBoundDetector> {
on { predictLowerBound(LowerBoundType.STATE) } doReturn 4000 on { predictLowerBound(LowerBoundType.STATE, 0) } doReturn 4000
on { types() } doReturn setOf(LowerBoundType.STATE) on { types() } doReturn setOf(LowerBoundType.STATE)
} }
val boundService = LowerBoundServiceMock(mock<Upstream>(), listOf(detector)) val boundService = LowerBoundServiceMock(mock<Upstream>(), listOf(detector))
val bound = boundService.predictLowerBound(LowerBoundType.STATE) val bound = boundService.predictLowerBound(LowerBoundType.STATE, 0)
verify(detector).types() verify(detector).types()
verify(detector).predictLowerBound(LowerBoundType.STATE) verify(detector).predictLowerBound(LowerBoundType.STATE, 0)
assertThat(bound).isEqualTo(4000) assertThat(bound).isEqualTo(4000)
} }
@@ -35,10 +35,10 @@ class LowerBoundServiceTest {
} }
val boundService = LowerBoundServiceMock(mock<Upstream>(), listOf(detector)) val boundService = LowerBoundServiceMock(mock<Upstream>(), listOf(detector))
val bound = boundService.predictLowerBound(LowerBoundType.BLOCK) val bound = boundService.predictLowerBound(LowerBoundType.BLOCK, 0)
verify(detector).types() verify(detector).types()
verify(detector, never()).predictLowerBound(any()) verify(detector, never()).predictLowerBound(any(), any())
assertThat(bound).isEqualTo(0) assertThat(bound).isEqualTo(0)
} }

View File

@@ -16,7 +16,7 @@ class LowerBoundsPredictionTest {
lowerBounds.updateBound(newLowerBound) lowerBounds.updateBound(newLowerBound)
val lastBound = lowerBounds.getLastBound(LowerBoundType.STATE) val lastBound = lowerBounds.getLastBound(LowerBoundType.STATE)
val predictedNextBound = lowerBounds.predictNextBound(LowerBoundType.STATE) val predictedNextBound = lowerBounds.predictNextBound(LowerBoundType.STATE, 0)
val allBounds = lowerBounds.getAllBounds(LowerBoundType.STATE) val allBounds = lowerBounds.getAllBounds(LowerBoundType.STATE)
assertThat(lastBound).isEqualTo(newLowerBound) assertThat(lastBound).isEqualTo(newLowerBound)
@@ -29,7 +29,7 @@ class LowerBoundsPredictionTest {
val lowerBounds = LowerBounds(Chain.ETHEREUM__MAINNET) val lowerBounds = LowerBounds(Chain.ETHEREUM__MAINNET)
val lastBound = lowerBounds.getLastBound(LowerBoundType.STATE) val lastBound = lowerBounds.getLastBound(LowerBoundType.STATE)
val predictedNextBound = lowerBounds.predictNextBound(LowerBoundType.STATE) val predictedNextBound = lowerBounds.predictNextBound(LowerBoundType.STATE, 0)
val allBounds = lowerBounds.getAllBounds(LowerBoundType.STATE) val allBounds = lowerBounds.getAllBounds(LowerBoundType.STATE)
assertThat(lastBound).isNull() assertThat(lastBound).isNull()
@@ -46,7 +46,7 @@ class LowerBoundsPredictionTest {
lowerBounds.updateBound(newLowerBound) lowerBounds.updateBound(newLowerBound)
val lastBound = lowerBounds.getLastBound(LowerBoundType.STATE) val lastBound = lowerBounds.getLastBound(LowerBoundType.STATE)
val predictedNextBound = lowerBounds.predictNextBound(LowerBoundType.STATE) val predictedNextBound = lowerBounds.predictNextBound(LowerBoundType.STATE, 0)
val allBounds = lowerBounds.getAllBounds(LowerBoundType.STATE) val allBounds = lowerBounds.getAllBounds(LowerBoundType.STATE)
assertThat(lastBound).isEqualTo(newLowerBound) assertThat(lastBound).isEqualTo(newLowerBound)
@@ -56,7 +56,7 @@ class LowerBoundsPredictionTest {
lowerBounds.updateBound(nextNewLowerBound) lowerBounds.updateBound(nextNewLowerBound)
val newLastBound = lowerBounds.getLastBound(LowerBoundType.STATE) val newLastBound = lowerBounds.getLastBound(LowerBoundType.STATE)
val newPredictedNextBound = lowerBounds.predictNextBound(LowerBoundType.STATE) val newPredictedNextBound = lowerBounds.predictNextBound(LowerBoundType.STATE, 0)
val newAllBounds = lowerBounds.getAllBounds(LowerBoundType.STATE) val newAllBounds = lowerBounds.getAllBounds(LowerBoundType.STATE)
assertThat(newLastBound).isEqualTo(nextNewLowerBound) assertThat(newLastBound).isEqualTo(nextNewLowerBound)
@@ -73,7 +73,7 @@ class LowerBoundsPredictionTest {
lowerBounds.updateBound(LowerBoundData(100000L, 1000, LowerBoundType.STATE)) lowerBounds.updateBound(LowerBoundData(100000L, 1000, LowerBoundType.STATE))
val lastBound = lowerBounds.getLastBound(LowerBoundType.STATE) val lastBound = lowerBounds.getLastBound(LowerBoundType.STATE)
val predictedNextBound = lowerBounds.predictNextBound(LowerBoundType.STATE) val predictedNextBound = lowerBounds.predictNextBound(LowerBoundType.STATE, 0)
val allBounds = lowerBounds.getAllBounds(LowerBoundType.STATE) val allBounds = lowerBounds.getAllBounds(LowerBoundType.STATE)
assertThat(lastBound).isEqualTo(newLowerBound) assertThat(lastBound).isEqualTo(newLowerBound)
@@ -162,7 +162,7 @@ class LowerBoundsPredictionTest {
lowerBounds.updateBound(lowerBound3) lowerBounds.updateBound(lowerBound3)
assertThat(lowerBounds.getLastBound(LowerBoundType.STATE)).isEqualTo(lowerBound3) assertThat(lowerBounds.getLastBound(LowerBoundType.STATE)).isEqualTo(lowerBound3)
assertThat(lowerBounds.predictNextBound(LowerBoundType.STATE)).isEqualTo(15060L) assertThat(lowerBounds.predictNextBound(LowerBoundType.STATE, 0)).isEqualTo(15060L)
assertThat(lowerBounds.getAllBounds(LowerBoundType.STATE)).isEqualTo(listOf(lowerBound1, lowerBound2, lowerBound3)) assertThat(lowerBounds.getAllBounds(LowerBoundType.STATE)).isEqualTo(listOf(lowerBound1, lowerBound2, lowerBound3))
} }
@@ -178,7 +178,7 @@ class LowerBoundsPredictionTest {
lowerBounds.updateBound(lowerBound2) lowerBounds.updateBound(lowerBound2)
lowerBounds.updateBound(lowerBound3) lowerBounds.updateBound(lowerBound3)
val predicted = lowerBounds.predictNextBound(LowerBoundType.STATE) val predicted = lowerBounds.predictNextBound(LowerBoundType.STATE, 0)
assertThat(predicted) assertThat(predicted)
.isLessThan(37996030) .isLessThan(37996030)
@@ -193,7 +193,7 @@ class LowerBoundsPredictionTest {
lowerBounds.updateBound(lowerBound1) lowerBounds.updateBound(lowerBound1)
val predicted = lowerBounds.predictNextBound(LowerBoundType.STATE) val predicted = lowerBounds.predictNextBound(LowerBoundType.STATE, 0)
assertThat(predicted) assertThat(predicted)
.isLessThan(37996030) .isLessThan(37996030)