slot height matcher implementation (#353)
This commit is contained in:
Submodule emerald-grpc updated: 6c969f4e74...a952f47fbe
@@ -151,6 +151,10 @@ abstract class AbstractHead @JvmOverloads constructor(
|
|||||||
return getCurrent()?.height
|
return getCurrent()?.height
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getCurrentSlotHeight(): Long? {
|
||||||
|
return getCurrent()?.slot
|
||||||
|
}
|
||||||
|
|
||||||
override fun stop() {
|
override fun stop() {
|
||||||
stopping = true
|
stopping = true
|
||||||
future?.let {
|
future?.let {
|
||||||
|
|||||||
@@ -31,6 +31,10 @@ class EmptyHead : Head {
|
|||||||
override fun getCurrentHeight(): Long? {
|
override fun getCurrentHeight(): Long? {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getCurrentSlotHeight(): Long? {
|
||||||
|
return null
|
||||||
|
}
|
||||||
override fun start() {
|
override fun start() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,6 +38,8 @@ interface Head {
|
|||||||
|
|
||||||
fun getCurrentHeight(): Long?
|
fun getCurrentHeight(): Long?
|
||||||
|
|
||||||
|
fun getCurrentSlotHeight(): Long?
|
||||||
|
|
||||||
fun start()
|
fun start()
|
||||||
|
|
||||||
fun stop()
|
fun stop()
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ sealed class MatchesResponse {
|
|||||||
is ExistsResponse -> "Label ${this.name} does not exist"
|
is ExistsResponse -> "Label ${this.name} does not exist"
|
||||||
GrpcResponse -> "Upstream is not grpc"
|
GrpcResponse -> "Upstream is not grpc"
|
||||||
is HeightResponse -> "Upstream height ${this.currentHeight} is less than ${this.height}"
|
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 MethodResponse -> "Method ${this.method} is not supported"
|
||||||
is MultiResponse ->
|
is MultiResponse ->
|
||||||
this.allResponses
|
this.allResponses
|
||||||
@@ -75,6 +76,11 @@ sealed class MatchesResponse {
|
|||||||
val currentHeight: Long,
|
val currentHeight: Long,
|
||||||
) : MatchesResponse()
|
) : MatchesResponse()
|
||||||
|
|
||||||
|
data class SlotHeightResponse(
|
||||||
|
val slot: Long,
|
||||||
|
val currentSlotHeight: Long,
|
||||||
|
) : MatchesResponse()
|
||||||
|
|
||||||
data class SameNodeResponse(
|
data class SameNodeResponse(
|
||||||
val upstreamHash: Byte,
|
val upstreamHash: Byte,
|
||||||
) : MatchesResponse()
|
) : MatchesResponse()
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import io.emeraldpay.dshackle.upstream.MatchesResponse.GrpcResponse
|
|||||||
import io.emeraldpay.dshackle.upstream.MatchesResponse.HeightResponse
|
import io.emeraldpay.dshackle.upstream.MatchesResponse.HeightResponse
|
||||||
import io.emeraldpay.dshackle.upstream.MatchesResponse.NotMatchedResponse
|
import io.emeraldpay.dshackle.upstream.MatchesResponse.NotMatchedResponse
|
||||||
import io.emeraldpay.dshackle.upstream.MatchesResponse.SameNodeResponse
|
import io.emeraldpay.dshackle.upstream.MatchesResponse.SameNodeResponse
|
||||||
|
import io.emeraldpay.dshackle.upstream.MatchesResponse.SlotHeightResponse
|
||||||
import io.emeraldpay.dshackle.upstream.MatchesResponse.Success
|
import io.emeraldpay.dshackle.upstream.MatchesResponse.Success
|
||||||
import org.apache.commons.lang3.StringUtils
|
import org.apache.commons.lang3.StringUtils
|
||||||
import java.util.Collections
|
import java.util.Collections
|
||||||
@@ -44,6 +45,9 @@ class Selector {
|
|||||||
return selectors
|
return selectors
|
||||||
.map {
|
.map {
|
||||||
when {
|
when {
|
||||||
|
it.hasSlotHeightSelector() -> {
|
||||||
|
SlotMatcher(it.slotHeightSelector.slotHeight)
|
||||||
|
}
|
||||||
it.hasHeightSelector() -> {
|
it.hasHeightSelector() -> {
|
||||||
val height = if (it.heightSelector.height == -1L) head.getCurrentHeight() else it.heightSelector.height
|
val height = if (it.heightSelector.height == -1L) head.getCurrentHeight() else it.heightSelector.height
|
||||||
if (height == null) {
|
if (height == null) {
|
||||||
@@ -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() {
|
class SameNodeMatcher(private val upstreamHash: Byte) : Matcher() {
|
||||||
|
|
||||||
override fun matchesWithCause(up: Upstream): MatchesResponse =
|
override fun matchesWithCause(up: Upstream): MatchesResponse =
|
||||||
|
|||||||
@@ -63,6 +63,10 @@ class EnrichedMergedHead constructor(
|
|||||||
return referenceHead.getCurrentHeight()
|
return referenceHead.getCurrentHeight()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getCurrentSlotHeight(): Long? {
|
||||||
|
return referenceHead.getCurrentSlotHeight()
|
||||||
|
}
|
||||||
|
|
||||||
override fun isRunning(): Boolean {
|
override fun isRunning(): Boolean {
|
||||||
return cacheSub != null
|
return cacheSub != null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,6 +68,11 @@ class EthereumHeadMock implements Head {
|
|||||||
return latest?.height
|
return latest?.height
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Long getCurrentSlotHeight() {
|
||||||
|
return latest?.slot
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void start() {
|
void start() {
|
||||||
|
|
||||||
|
|||||||
@@ -462,7 +462,7 @@ class FilteredApisSpec extends Specification {
|
|||||||
_ * getRole() >> UpstreamsConfig.UpstreamRole.PRIMARY
|
_ * getRole() >> UpstreamsConfig.UpstreamRole.PRIMARY
|
||||||
_ * isAvailable() >> true
|
_ * isAvailable() >> true
|
||||||
_ * getHead() >> Mock(Head) {
|
_ * getHead() >> Mock(Head) {
|
||||||
_ * getCurrentHeight() >> 100000001
|
_ * getCurrentSlotHeight() >> 100000001
|
||||||
}
|
}
|
||||||
_ * getStatus() >> UpstreamAvailability.OK
|
_ * getStatus() >> UpstreamAvailability.OK
|
||||||
_ * getLabels() >> of(UpstreamsConfig.Labels.fromMap(Map.of("node", "test")))
|
_ * getLabels() >> of(UpstreamsConfig.Labels.fromMap(Map.of("node", "test")))
|
||||||
@@ -474,7 +474,7 @@ class FilteredApisSpec extends Specification {
|
|||||||
_ * getId() >> "id1"
|
_ * getId() >> "id1"
|
||||||
_ * getStatus() >> UpstreamAvailability.OK
|
_ * getStatus() >> UpstreamAvailability.OK
|
||||||
_ * getHead() >> Mock(Head) {
|
_ * getHead() >> Mock(Head) {
|
||||||
_ * getCurrentHeight() >> 100000
|
_ * getCurrentSlotHeight() >> 100000
|
||||||
}
|
}
|
||||||
_ * getLabels() >> of(UpstreamsConfig.Labels.fromMap(Map.of("node", "archive")))
|
_ * getLabels() >> of(UpstreamsConfig.Labels.fromMap(Map.of("node", "archive")))
|
||||||
}, up
|
}, up
|
||||||
@@ -484,7 +484,7 @@ class FilteredApisSpec extends Specification {
|
|||||||
Chain.ETHEREUM__MAINNET, ups,
|
Chain.ETHEREUM__MAINNET, ups,
|
||||||
new Selector.MultiMatcher(
|
new Selector.MultiMatcher(
|
||||||
of(
|
of(
|
||||||
new Selector.HeightMatcher(100000000),
|
new Selector.SlotMatcher(100000000),
|
||||||
new Selector.LabelMatcher("node", of("test"))
|
new Selector.LabelMatcher("node", of("test"))
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -32,6 +32,21 @@ class SelectorSpec extends Specification {
|
|||||||
private BlockchainOuterClass.Selector selectLabel2Selector = BlockchainOuterClass.Selector.newBuilder()
|
private BlockchainOuterClass.Selector selectLabel2Selector = BlockchainOuterClass.Selector.newBuilder()
|
||||||
.setLabelSelector(selectLabel2).build()
|
.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"() {
|
def "Convert height selector"() {
|
||||||
setup:
|
setup:
|
||||||
def heightSelector = BlockchainOuterClass.Selector.newBuilder()
|
def heightSelector = BlockchainOuterClass.Selector.newBuilder()
|
||||||
|
|||||||
Reference in New Issue
Block a user