Ignore blocks from syncing nodes (#243)
This commit is contained in:
@@ -26,9 +26,9 @@ import io.emeraldpay.dshackle.data.BlockContainer
|
||||
import io.emeraldpay.dshackle.test.EthereumPosRpcUpstreamMock
|
||||
import io.emeraldpay.dshackle.test.MultistreamHolderMock
|
||||
import io.emeraldpay.dshackle.test.TestingCommons
|
||||
import io.emeraldpay.dshackle.upstream.ethereum.EthereumPosRpcUpstream
|
||||
import io.emeraldpay.etherjar.domain.BlockHash
|
||||
import io.emeraldpay.dshackle.upstream.ethereum.EthereumLikeRpcUpstream
|
||||
import io.emeraldpay.dshackle.upstream.ethereum.json.BlockJson
|
||||
import io.emeraldpay.etherjar.domain.BlockHash
|
||||
import io.emeraldpay.etherjar.rpc.json.TransactionRefJson
|
||||
import reactor.core.publisher.Mono
|
||||
import reactor.test.StepVerifier
|
||||
@@ -43,7 +43,7 @@ class StreamHeadSpec extends Specification {
|
||||
|
||||
def "Errors on unavailable chain"() {
|
||||
setup:
|
||||
def upstreams = new MultistreamHolderMock(Chain.ETHEREUM__MAINNET, Stub(EthereumPosRpcUpstream))
|
||||
def upstreams = new MultistreamHolderMock(Chain.ETHEREUM__MAINNET, Stub(EthereumLikeRpcUpstream))
|
||||
def streamHead = new StreamHead(upstreams)
|
||||
when:
|
||||
def flux = streamHead.add(
|
||||
|
||||
@@ -79,7 +79,7 @@ class EthereumHeadMock implements Head {
|
||||
}
|
||||
|
||||
@Override
|
||||
void onNoHeadUpdates() {
|
||||
void onSyncingNode(boolean isSyncing) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,19 +24,14 @@ import io.emeraldpay.dshackle.data.BlockContainer
|
||||
import io.emeraldpay.dshackle.reader.Reader
|
||||
import io.emeraldpay.dshackle.startup.QuorumForLabels
|
||||
import io.emeraldpay.dshackle.upstream.UpstreamAvailability
|
||||
import io.emeraldpay.dshackle.upstream.calls.AggregatedCallMethods
|
||||
import io.emeraldpay.dshackle.upstream.calls.CallMethods
|
||||
import io.emeraldpay.dshackle.upstream.calls.DefaultBitcoinMethods
|
||||
import io.emeraldpay.dshackle.upstream.calls.DefaultEthereumMethods
|
||||
import io.emeraldpay.dshackle.upstream.calls.DirectCallMethods
|
||||
import io.emeraldpay.dshackle.upstream.ethereum.EthereumPosRpcUpstream
|
||||
import io.emeraldpay.dshackle.upstream.calls.*
|
||||
import io.emeraldpay.dshackle.upstream.ethereum.EthereumLikeRpcUpstream
|
||||
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcRequest
|
||||
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcResponse
|
||||
import org.jetbrains.annotations.NotNull
|
||||
import org.reactivestreams.Publisher
|
||||
|
||||
|
||||
class EthereumPosRpcUpstreamMock extends EthereumPosRpcUpstream {
|
||||
class EthereumPosRpcUpstreamMock extends EthereumLikeRpcUpstream {
|
||||
EthereumHeadMock ethereumHeadMock
|
||||
|
||||
|
||||
@@ -75,7 +70,9 @@ class EthereumPosRpcUpstreamMock extends EthereumPosRpcUpstream {
|
||||
methods,
|
||||
new QuorumForLabels.QuorumItem(1, UpstreamsConfig.Labels.fromMap(labels)),
|
||||
new ConnectorFactoryMock(api, new EthereumHeadMock()),
|
||||
ChainsConfig.ChainConfig.default())
|
||||
ChainsConfig.ChainConfig.default(),
|
||||
true
|
||||
)
|
||||
this.ethereumHeadMock = this.getHead() as EthereumHeadMock
|
||||
setLag(0)
|
||||
setStatus(UpstreamAvailability.OK)
|
||||
|
||||
@@ -23,18 +23,14 @@ import io.emeraldpay.dshackle.data.BlockContainer
|
||||
import io.emeraldpay.dshackle.reader.Reader
|
||||
import io.emeraldpay.dshackle.startup.QuorumForLabels
|
||||
import io.emeraldpay.dshackle.upstream.UpstreamAvailability
|
||||
import io.emeraldpay.dshackle.upstream.calls.AggregatedCallMethods
|
||||
import io.emeraldpay.dshackle.upstream.calls.CallMethods
|
||||
import io.emeraldpay.dshackle.upstream.calls.DefaultBitcoinMethods
|
||||
import io.emeraldpay.dshackle.upstream.calls.DefaultEthereumMethods
|
||||
import io.emeraldpay.dshackle.upstream.calls.DirectCallMethods
|
||||
import io.emeraldpay.dshackle.upstream.ethereum.EthereumRpcUpstream
|
||||
import io.emeraldpay.dshackle.upstream.calls.*
|
||||
import io.emeraldpay.dshackle.upstream.ethereum.EthereumLikeRpcUpstream
|
||||
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcRequest
|
||||
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcResponse
|
||||
import org.jetbrains.annotations.NotNull
|
||||
import org.reactivestreams.Publisher
|
||||
|
||||
class EthereumRpcUpstreamMock extends EthereumRpcUpstream {
|
||||
class EthereumRpcUpstreamMock extends EthereumLikeRpcUpstream {
|
||||
EthereumHeadMock ethereumHeadMock
|
||||
|
||||
|
||||
@@ -65,7 +61,9 @@ class EthereumRpcUpstreamMock extends EthereumRpcUpstream {
|
||||
methods,
|
||||
new QuorumForLabels.QuorumItem(1, new UpstreamsConfig.Labels()),
|
||||
new ConnectorFactoryMock(api, new EthereumHeadMock()),
|
||||
ChainsConfig.ChainConfig.default())
|
||||
ChainsConfig.ChainConfig.default(),
|
||||
false
|
||||
)
|
||||
this.ethereumHeadMock = this.getHead() as EthereumHeadMock
|
||||
setLag(0)
|
||||
setStatus(UpstreamAvailability.OK)
|
||||
|
||||
@@ -25,10 +25,9 @@ import io.emeraldpay.dshackle.upstream.bitcoin.BitcoinRpcUpstream
|
||||
import io.emeraldpay.dshackle.upstream.calls.CallMethods
|
||||
import io.emeraldpay.dshackle.upstream.calls.DefaultEthereumMethods
|
||||
import io.emeraldpay.dshackle.upstream.ethereum.EthereumCachingReader
|
||||
import io.emeraldpay.dshackle.upstream.ethereum.EthereumLikeRpcUpstream
|
||||
import io.emeraldpay.dshackle.upstream.ethereum.EthereumPosMultiStream
|
||||
import io.emeraldpay.dshackle.upstream.ethereum.EthereumPosRpcUpstream
|
||||
import org.jetbrains.annotations.NotNull
|
||||
import org.springframework.cloud.sleuth.Tracer
|
||||
import org.springframework.cloud.sleuth.brave.bridge.BraveTracer
|
||||
import reactor.core.scheduler.Schedulers
|
||||
|
||||
@@ -46,9 +45,9 @@ class MultistreamHolderMock implements MultistreamHolder {
|
||||
if (BlockchainType.from(chain) == BlockchainType.EVM_POS) {
|
||||
if (up instanceof EthereumPosMultiStream) {
|
||||
upstreams[chain] = up
|
||||
} else if (up instanceof EthereumPosRpcUpstream) {
|
||||
} else if (up instanceof EthereumLikeRpcUpstream) {
|
||||
upstreams[chain] = new EthereumPosMultiStream(
|
||||
chain, [up as EthereumPosRpcUpstream], Caches.default(),
|
||||
chain, [up as EthereumLikeRpcUpstream], Caches.default(),
|
||||
Schedulers.parallel(), TestingCommons.tracerMock()
|
||||
)
|
||||
} else {
|
||||
@@ -97,15 +96,15 @@ class MultistreamHolderMock implements MultistreamHolder {
|
||||
CallMethods customMethods = null
|
||||
Head customHead = null
|
||||
|
||||
EthereumMultistreamMock(@NotNull Chain chain, @NotNull List<EthereumPosRpcUpstream> upstreams, @NotNull Caches caches) {
|
||||
EthereumMultistreamMock(@NotNull Chain chain, @NotNull List<EthereumLikeRpcUpstream> upstreams, @NotNull Caches caches) {
|
||||
super(chain, upstreams, caches, Schedulers.parallel(), new BraveTracer(null, null, null))
|
||||
}
|
||||
|
||||
EthereumMultistreamMock(@NotNull Chain chain, @NotNull List<EthereumPosRpcUpstream> upstreams) {
|
||||
EthereumMultistreamMock(@NotNull Chain chain, @NotNull List<EthereumLikeRpcUpstream> upstreams) {
|
||||
this(chain, upstreams, Caches.default())
|
||||
}
|
||||
|
||||
EthereumMultistreamMock(@NotNull Chain chain, @NotNull EthereumPosRpcUpstream upstream) {
|
||||
EthereumMultistreamMock(@NotNull Chain chain, @NotNull EthereumLikeRpcUpstream upstream) {
|
||||
this(chain, [upstream])
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ import io.emeraldpay.dshackle.startup.QuorumForLabels
|
||||
import io.emeraldpay.dshackle.test.EthereumApiStub
|
||||
import io.emeraldpay.dshackle.test.TestingCommons
|
||||
import io.emeraldpay.dshackle.upstream.calls.DefaultEthereumMethods
|
||||
import io.emeraldpay.dshackle.upstream.ethereum.EthereumRpcUpstream
|
||||
import io.emeraldpay.dshackle.upstream.ethereum.EthereumLikeRpcUpstream
|
||||
import io.emeraldpay.dshackle.upstream.ethereum.connectors.EthereumConnectorFactory
|
||||
import io.emeraldpay.dshackle.upstream.forkchoice.MostWorkForkChoice
|
||||
import reactor.core.scheduler.Schedulers
|
||||
@@ -42,7 +42,7 @@ class FilteredApisSpec extends Specification {
|
||||
def "Verifies labels"() {
|
||||
setup:
|
||||
def i = 0
|
||||
List<EthereumRpcUpstream> upstreams = [
|
||||
List<EthereumLikeRpcUpstream> upstreams = [
|
||||
[test: "foo"],
|
||||
[test: "bar"],
|
||||
[test: "foo", test2: "baz"],
|
||||
@@ -61,7 +61,7 @@ class FilteredApisSpec extends Specification {
|
||||
Schedulers.parallel(),
|
||||
Schedulers.parallel()
|
||||
)
|
||||
new EthereumRpcUpstream(
|
||||
new EthereumLikeRpcUpstream(
|
||||
"test",
|
||||
(byte) 123,
|
||||
Chain.ETHEREUM__MAINNET,
|
||||
@@ -70,7 +70,8 @@ class FilteredApisSpec extends Specification {
|
||||
ethereumTargets,
|
||||
new QuorumForLabels.QuorumItem(1, UpstreamsConfig.Labels.fromMap(it)),
|
||||
connectorFactory,
|
||||
ChainsConfig.ChainConfig.default()
|
||||
ChainsConfig.ChainConfig.default(),
|
||||
false
|
||||
)
|
||||
}
|
||||
def matcher = new Selector.LabelMatcher("test", ["foo"])
|
||||
|
||||
@@ -27,8 +27,8 @@ import io.emeraldpay.dshackle.startup.UpstreamChangeEvent
|
||||
import io.emeraldpay.dshackle.test.EthereumPosRpcUpstreamMock
|
||||
import io.emeraldpay.dshackle.test.TestingCommons
|
||||
import io.emeraldpay.dshackle.upstream.calls.DirectCallMethods
|
||||
import io.emeraldpay.dshackle.upstream.ethereum.EthereumLikeRpcUpstream
|
||||
import io.emeraldpay.dshackle.upstream.ethereum.EthereumPosMultiStream
|
||||
import io.emeraldpay.dshackle.upstream.ethereum.EthereumPosUpstream
|
||||
import io.emeraldpay.dshackle.upstream.ethereum.json.BlockJson
|
||||
import io.emeraldpay.dshackle.upstream.grpc.EthereumPosGrpcUpstream
|
||||
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcRequest
|
||||
@@ -252,7 +252,7 @@ class MultistreamSpec extends Specification {
|
||||
setup:
|
||||
def up1 = new EthereumPosRpcUpstreamMock("test1", Chain.ETHEREUM__MAINNET, TestingCommons.api(), new DirectCallMethods(["eth_test1", "eth_test2", "eth_test3"]))
|
||||
def up2 = new EthereumPosRpcUpstreamMock("test2", Chain.ETHEREUM__MAINNET, TestingCommons.api(), new DirectCallMethods(["eth_test1", "eth_test2"]))
|
||||
def ms = new EthereumPosMultiStream(Chain.ETHEREUM__MAINNET, new ArrayList<EthereumPosUpstream>(), Caches.default(), Schedulers.parallel(), TestingCommons.tracerMock())
|
||||
def ms = new EthereumPosMultiStream(Chain.ETHEREUM__MAINNET, new ArrayList<EthereumLikeRpcUpstream>(), Caches.default(), Schedulers.parallel(), TestingCommons.tracerMock())
|
||||
when:
|
||||
ms.onUpstreamChange(
|
||||
new UpstreamChangeEvent(Chain.ETHEREUM__MAINNET, up1, UpstreamChangeEvent.ChangeType.ADDED)
|
||||
@@ -305,7 +305,7 @@ class MultistreamSpec extends Specification {
|
||||
setup:
|
||||
def up1 = new EthereumPosRpcUpstreamMock("test1", Chain.ETHEREUM__MAINNET, TestingCommons.api(), new DirectCallMethods(["eth_test1", "eth_test2", "eth_test3"]))
|
||||
def up2 = new EthereumPosRpcUpstreamMock("test2", Chain.ETHEREUM__MAINNET, TestingCommons.api(), new DirectCallMethods(["eth_test1", "eth_test2"]))
|
||||
def ms = new EthereumPosMultiStream(Chain.ETHEREUM__MAINNET, new ArrayList<EthereumPosUpstream>(), Caches.default(), Schedulers.parallel(), TestingCommons.tracerMock())
|
||||
def ms = new EthereumPosMultiStream(Chain.ETHEREUM__MAINNET, new ArrayList<EthereumLikeRpcUpstream>(), Caches.default(), Schedulers.parallel(), TestingCommons.tracerMock())
|
||||
def head1 = createBlock(250, "0x0d050c785de17179f935b9b93aca09c442964cc59972c71ae68e74731448401b")
|
||||
def head2 = createBlock(270, "0x0d050c785de17179f935b9b93aca09c442964cc59972c71ae68e74731448402b")
|
||||
def head3 = createBlock(100, "0x0d050c785de17179f935b9b93aca09c442964cc59972c71ae68e74731448412b")
|
||||
@@ -339,7 +339,7 @@ class MultistreamSpec extends Specification {
|
||||
|
||||
class TestEthereumPosMultistream extends EthereumPosMultiStream {
|
||||
|
||||
TestEthereumPosMultistream(@NotNull Chain chain, @NotNull List<EthereumPosUpstream> upstreams, @NotNull Caches caches) {
|
||||
TestEthereumPosMultistream(@NotNull Chain chain, @NotNull List<EthereumLikeRpcUpstream> upstreams, @NotNull Caches caches) {
|
||||
super(chain, upstreams, caches, Schedulers.parallel(), TestingCommons.tracerMock())
|
||||
}
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ class ERC20BalanceSpec extends Specification {
|
||||
JsonRpcResponse.ok('"0x0000000000000000000000000000000000000000000000000000001f28d72868"')
|
||||
)
|
||||
|
||||
EthereumPosRpcUpstream upstream = new EthereumPosRpcUpstreamMock(Chain.ETHEREUM__MAINNET, api)
|
||||
EthereumLikeRpcUpstream upstream = new EthereumPosRpcUpstreamMock(Chain.ETHEREUM__MAINNET, api)
|
||||
ERC20Token token = new ERC20Token(Address.from("0x54EedeAC495271d0F6B175474E89094C44Da98b9"))
|
||||
ERC20Balance query = new ERC20Balance()
|
||||
|
||||
@@ -73,7 +73,7 @@ class ERC20BalanceSpec extends Specification {
|
||||
JsonRpcResponse.ok('"0x0000000000000000000000000000000000000000000000000000001f28d72868"')
|
||||
)
|
||||
|
||||
EthereumPosRpcUpstream upstream = new EthereumPosRpcUpstreamMock(Chain.ETHEREUM__MAINNET, api)
|
||||
EthereumLikeRpcUpstream upstream = new EthereumPosRpcUpstreamMock(Chain.ETHEREUM__MAINNET, api)
|
||||
ERC20Token token = new ERC20Token(Address.from("0x54EedeAC495271d0F6B175474E89094C44Da98b9"))
|
||||
ERC20Balance query = new ERC20Balance()
|
||||
|
||||
|
||||
@@ -16,9 +16,13 @@
|
||||
package io.emeraldpay.dshackle.upstream.ethereum
|
||||
|
||||
import io.emeraldpay.dshackle.config.UpstreamsConfig
|
||||
import io.emeraldpay.dshackle.reader.Reader
|
||||
import io.emeraldpay.dshackle.test.ApiReaderMock
|
||||
import io.emeraldpay.dshackle.test.TestingCommons
|
||||
import io.emeraldpay.dshackle.upstream.Head
|
||||
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcResponse
|
||||
import io.emeraldpay.etherjar.rpc.RpcResponseError
|
||||
import reactor.core.publisher.Mono
|
||||
import reactor.util.function.Tuples
|
||||
import spock.lang.Specification
|
||||
|
||||
@@ -30,7 +34,7 @@ class EthereumUpstreamValidatorSpec extends Specification {
|
||||
|
||||
def "Resolve to final availability"() {
|
||||
setup:
|
||||
def validator = new EthereumUpstreamValidator(Stub(EthereumUpstream), UpstreamsConfig.PartialOptions.getDefaults().buildOptions())
|
||||
def validator = new EthereumUpstreamValidator(Stub(EthereumLikeUpstream), UpstreamsConfig.PartialOptions.getDefaults().buildOptions())
|
||||
expect:
|
||||
validator.resolve(Tuples.of(sync, peers)) == exp
|
||||
where:
|
||||
@@ -51,7 +55,7 @@ class EthereumUpstreamValidatorSpec extends Specification {
|
||||
def options = UpstreamsConfig.PartialOptions.getDefaults().tap {
|
||||
it.validateSyncing = false
|
||||
}.buildOptions()
|
||||
def up = Mock(EthereumUpstream)
|
||||
def up = Mock(EthereumLikeUpstream)
|
||||
def validator = new EthereumUpstreamValidator(up, options)
|
||||
|
||||
when:
|
||||
@@ -79,6 +83,33 @@ class EthereumUpstreamValidatorSpec extends Specification {
|
||||
act == OK
|
||||
}
|
||||
|
||||
def "Execute onSyncingNode with result of eth_syncing"() {
|
||||
setup:
|
||||
def options = UpstreamsConfig.PartialOptions.getDefaults().tap {
|
||||
it.validateSyncing = true
|
||||
}.buildOptions()
|
||||
def up = Mock(EthereumLikeUpstream) {
|
||||
2 * getIngressReader() >> Mock(Reader) { reader ->
|
||||
2 * reader.read(_) >>> [
|
||||
Mono.just(new JsonRpcResponse('true'.getBytes(), null)),
|
||||
Mono.just(new JsonRpcResponse('false'.getBytes(), null))
|
||||
]
|
||||
}
|
||||
2 * getHead() >> Mock(Head) {head ->
|
||||
1 * head.onSyncingNode(true)
|
||||
1 * head.onSyncingNode(false)
|
||||
}
|
||||
}
|
||||
def validator = new EthereumUpstreamValidator(up, options)
|
||||
|
||||
when:
|
||||
def act = validator.validateSyncing().block(Duration.ofSeconds(1))
|
||||
def act2 = validator.validateSyncing().block(Duration.ofSeconds(1))
|
||||
then:
|
||||
act == SYNCING
|
||||
act2 == OK
|
||||
}
|
||||
|
||||
def "Syncing is SYNCING when state returned from upstream"() {
|
||||
setup:
|
||||
def options = UpstreamsConfig.PartialOptions.getDefaults().tap {
|
||||
@@ -121,7 +152,7 @@ class EthereumUpstreamValidatorSpec extends Specification {
|
||||
it.validatePeers = false
|
||||
it.minPeers = 10
|
||||
}.buildOptions()
|
||||
def up = Mock(EthereumUpstream)
|
||||
def up = Mock(EthereumLikeUpstream)
|
||||
def validator = new EthereumUpstreamValidator(up, options)
|
||||
|
||||
when:
|
||||
@@ -137,7 +168,7 @@ class EthereumUpstreamValidatorSpec extends Specification {
|
||||
it.validatePeers = true
|
||||
it.minPeers = 0
|
||||
}.buildOptions()
|
||||
def up = Mock(EthereumUpstream)
|
||||
def up = Mock(EthereumLikeUpstream)
|
||||
def validator = new EthereumUpstreamValidator(up, options)
|
||||
|
||||
when:
|
||||
|
||||
@@ -257,4 +257,69 @@ class EthereumWsHeadSpec extends Specification {
|
||||
.thenCancel()
|
||||
.verify(Duration.ofSeconds(1))
|
||||
}
|
||||
|
||||
def "Reset current subscription if upstream is syncing and then restore it"() {
|
||||
setup:
|
||||
def block = new BlockJson<TransactionRefJson>()
|
||||
block.timestamp = Instant.now().truncatedTo(ChronoUnit.SECONDS)
|
||||
block.number = 103
|
||||
block.parentHash = parent
|
||||
block.hash = BlockHash.from("0x3ec2ebf5d0ec474d0ac6bc50d2770d8409ad76e119968e7919f85d5ec8915200")
|
||||
def secondBlock = new BlockJson<TransactionRefJson>()
|
||||
secondBlock.parentHash = parent
|
||||
secondBlock.timestamp = Instant.now().truncatedTo(ChronoUnit.SECONDS)
|
||||
secondBlock.number = 105
|
||||
secondBlock.hash = BlockHash.from("0x29229361dc5aa1ec66c323dc7a299e2b61a8c8dd2a3522d41255ec10eca25dd8")
|
||||
|
||||
def firstHeadBlock = block.with {
|
||||
Global.objectMapper.writeValueAsBytes(it)
|
||||
}
|
||||
def secondHeadBlock = secondBlock.with {
|
||||
Global.objectMapper.writeValueAsBytes(it)
|
||||
}
|
||||
|
||||
def apiMock = TestingCommons.api()
|
||||
def connectionInfoSink = Sinks.many().multicast().directBestEffort()
|
||||
apiMock.answerOnce("eth_getBlockByHash", ["0x3ec2ebf5d0ec474d0ac6bc50d2770d8409ad76e119968e7919f85d5ec8915200", false], null)
|
||||
apiMock.answerOnce("eth_blockNumber", [], Mono.empty())
|
||||
apiMock.answerOnce("eth_getBlockByHash", ["0x29229361dc5aa1ec66c323dc7a299e2b61a8c8dd2a3522d41255ec10eca25dd8", false], null)
|
||||
apiMock.answerOnce("eth_blockNumber", [], Mono.empty())
|
||||
|
||||
def ws = Mock(WsSubscriptions) {
|
||||
1 * it.connectionInfoFlux() >> connectionInfoSink.asFlux()
|
||||
2 * subscribe("newHeads") >>> [
|
||||
new WsSubscriptions.SubscribeData(Flux.fromIterable([firstHeadBlock]), "id"),
|
||||
new WsSubscriptions.SubscribeData(Flux.fromIterable([secondHeadBlock]), "id"),
|
||||
]
|
||||
}
|
||||
|
||||
def head = new EthereumWsHead("fake", new AlwaysForkChoice(), BlockValidator.ALWAYS_VALID, apiMock, ws, true, Schedulers.parallel(), Schedulers.parallel())
|
||||
|
||||
when:
|
||||
def act = head.getFlux()
|
||||
|
||||
then:
|
||||
StepVerifier.create(act)
|
||||
.then { head.start() }
|
||||
.expectNext(BlockContainer.from(block))
|
||||
.then {
|
||||
head.onSyncingNode(true)
|
||||
}
|
||||
.then {
|
||||
assert !head.isRunning()
|
||||
}
|
||||
.then {
|
||||
head.onNoHeadUpdates()
|
||||
}
|
||||
.then {
|
||||
assert !head.isRunning()
|
||||
}
|
||||
.then {
|
||||
head.onSyncingNode(false)
|
||||
head.onNoHeadUpdates()
|
||||
}
|
||||
.expectNext(BlockContainer.from(secondBlock))
|
||||
.thenCancel()
|
||||
.verify(Duration.ofSeconds(1))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user