Skip to content

Commit

Permalink
slot height matcher implementation (#353)
Browse files Browse the repository at this point in the history
  • Loading branch information
Termina1 authored Nov 28, 2023
1 parent bb3d568 commit 353a483
Show file tree
Hide file tree
Showing 10 changed files with 81 additions and 4 deletions.
2 changes: 1 addition & 1 deletion emerald-grpc
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,10 @@ abstract class AbstractHead @JvmOverloads constructor(
return getCurrent()?.height
}

override fun getCurrentSlotHeight(): Long? {
return getCurrent()?.slot
}

override fun stop() {
stopping = true
future?.let {
Expand Down
4 changes: 4 additions & 0 deletions src/main/kotlin/io/emeraldpay/dshackle/upstream/EmptyHead.kt
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ class EmptyHead : Head {
override fun getCurrentHeight(): Long? {
return null
}

override fun getCurrentSlotHeight(): Long? {
return null
}
override fun start() {
}

Expand Down
2 changes: 2 additions & 0 deletions src/main/kotlin/io/emeraldpay/dshackle/upstream/Head.kt
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ interface Head {

fun getCurrentHeight(): Long?

fun getCurrentSlotHeight(): Long?

fun start()

fun stop()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ sealed class MatchesResponse {
is ExistsResponse -> "Label ${this.name} does not exist"
GrpcResponse -> "Upstream is not grpc"
is HeightResponse -> "Upstream height ${this.currentHeight} is less than ${this.height}"
is SlotHeightResponse -> "Upstream slot height ${this.currentSlotHeight} is less than ${this.slot}"
is MethodResponse -> "Method ${this.method} is not supported"
is MultiResponse ->
this.allResponses
Expand Down Expand Up @@ -75,6 +76,11 @@ sealed class MatchesResponse {
val currentHeight: Long,
) : MatchesResponse()

data class SlotHeightResponse(
val slot: Long,
val currentSlotHeight: Long,
) : MatchesResponse()

data class SameNodeResponse(
val upstreamHash: Byte,
) : MatchesResponse()
Expand Down
37 changes: 37 additions & 0 deletions src/main/kotlin/io/emeraldpay/dshackle/upstream/Selector.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import io.emeraldpay.dshackle.upstream.MatchesResponse.GrpcResponse
import io.emeraldpay.dshackle.upstream.MatchesResponse.HeightResponse
import io.emeraldpay.dshackle.upstream.MatchesResponse.NotMatchedResponse
import io.emeraldpay.dshackle.upstream.MatchesResponse.SameNodeResponse
import io.emeraldpay.dshackle.upstream.MatchesResponse.SlotHeightResponse
import io.emeraldpay.dshackle.upstream.MatchesResponse.Success
import org.apache.commons.lang3.StringUtils
import java.util.Collections
Expand All @@ -44,6 +45,9 @@ class Selector {
return selectors
.map {
when {
it.hasSlotHeightSelector() -> {
SlotMatcher(it.slotHeightSelector.slotHeight)
}
it.hasHeightSelector() -> {
val height = if (it.heightSelector.height == -1L) head.getCurrentHeight() else it.heightSelector.height
if (height == null) {
Expand Down Expand Up @@ -488,6 +492,39 @@ class Selector {
}
}

class SlotMatcher(val slotHeight: Long) : Matcher() {

override fun matchesWithCause(up: Upstream): MatchesResponse {
val currentHeight = up.getHead().getCurrentSlotHeight() ?: 0
return if (currentHeight >= slotHeight) {
Success
} else {
SlotHeightResponse(slotHeight, currentHeight)
}
}

override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is SlotMatcher) return false

if (slotHeight != other.slotHeight) return false

return true
}

override fun hashCode(): Int {
return slotHeight.hashCode()
}

override fun describeInternal(): String {
return "slot height $slotHeight"
}

override fun toString(): String {
return "Matcher: ${describeInternal()}"
}
}

class SameNodeMatcher(private val upstreamHash: Byte) : Matcher() {

override fun matchesWithCause(up: Upstream): MatchesResponse =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ class EnrichedMergedHead constructor(
return referenceHead.getCurrentHeight()
}

override fun getCurrentSlotHeight(): Long? {
return referenceHead.getCurrentSlotHeight()
}

override fun isRunning(): Boolean {
return cacheSub != null
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,11 @@ class EthereumHeadMock implements Head {
return latest?.height
}

@Override
Long getCurrentSlotHeight() {
return latest?.slot
}

@Override
void start() {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -462,7 +462,7 @@ class FilteredApisSpec extends Specification {
_ * getRole() >> UpstreamsConfig.UpstreamRole.PRIMARY
_ * isAvailable() >> true
_ * getHead() >> Mock(Head) {
_ * getCurrentHeight() >> 100000001
_ * getCurrentSlotHeight() >> 100000001
}
_ * getStatus() >> UpstreamAvailability.OK
_ * getLabels() >> of(UpstreamsConfig.Labels.fromMap(Map.of("node", "test")))
Expand All @@ -474,7 +474,7 @@ class FilteredApisSpec extends Specification {
_ * getId() >> "id1"
_ * getStatus() >> UpstreamAvailability.OK
_ * getHead() >> Mock(Head) {
_ * getCurrentHeight() >> 100000
_ * getCurrentSlotHeight() >> 100000
}
_ * getLabels() >> of(UpstreamsConfig.Labels.fromMap(Map.of("node", "archive")))
}, up
Expand All @@ -484,7 +484,7 @@ class FilteredApisSpec extends Specification {
Chain.ETHEREUM__MAINNET, ups,
new Selector.MultiMatcher(
of(
new Selector.HeightMatcher(100000000),
new Selector.SlotMatcher(100000000),
new Selector.LabelMatcher("node", of("test"))
)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,21 @@ class SelectorSpec extends Specification {
private BlockchainOuterClass.Selector selectLabel2Selector = BlockchainOuterClass.Selector.newBuilder()
.setLabelSelector(selectLabel2).build()

def "Convert slot height selector"() {
setup:
def slotHeightSelector = BlockchainOuterClass.Selector.newBuilder()
.setSlotHeightSelector(
BlockchainOuterClass.SlotHeightSelector.newBuilder()
.setSlotHeight(10000)
.build()
)
.build()
when:
def act = Selector.convertToMatcher(List.of(slotHeightSelector), Stub(Head))
then:
act == new Selector.MultiMatcher(List.of(new Selector.SlotMatcher(10000)))
}

def "Convert height selector"() {
setup:
def heightSelector = BlockchainOuterClass.Selector.newBuilder()
Expand Down

0 comments on commit 353a483

Please sign in to comment.