Skip to content

Commit

Permalink
Feature: yaci-blockfrost and original_blockfrost separation as well a…
Browse files Browse the repository at this point in the history
…s ChainSyncService with HealthIndicator. (#85)

* Feature: yaci-blockfrost and original_blockfrost separation.

* Feature: implemented ChainSyncService as well as chain sync indicator.

---------

Co-authored-by: Mateusz Czeladka <mateusz.czeladka@cardanofoundation.org>
  • Loading branch information
matiwinnetou and Mateusz Czeladka authored Aug 25, 2023
1 parent b88eece commit 9821d06
Show file tree
Hide file tree
Showing 6 changed files with 130 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import org.cardano.foundation.voting.service.blockchain_state.backend_bridge.BackendServiceBlockchainDataChainTipService;
import org.cardano.foundation.voting.service.blockchain_state.backend_bridge.BackendServiceBlockchainDataStakePoolService;
import org.cardano.foundation.voting.service.blockchain_state.backend_bridge.BackendServiceBlockchainDataTransactionDetailsService;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
Expand All @@ -17,13 +18,14 @@
public class BlockchainDataConfig {

@Bean
public BlockchainDataChainTipService blockchainDataChainTipService(CardanoNetwork network, BackendService backendService) {
public BlockchainDataChainTipService blockchainDataChainTipService(CardanoNetwork network,
@Qualifier("yaci_blockfrost") BackendService backendService) {
return new BackendServiceBlockchainDataChainTipService(backendService, network);
}

@Bean
@Profile( value = { "prod", "dev--preprod"} )
public BlockchainDataStakePoolService blockchainDataStakePoolService(BackendService backendService) {
public BlockchainDataStakePoolService blockchainDataStakePoolService(@Qualifier("original_blockfrost") BackendService backendService) {
return new BackendServiceBlockchainDataStakePoolService(backendService);
}

Expand All @@ -34,7 +36,8 @@ public BlockchainDataStakePoolService dummyBlockchainDataStakePoolService() {
}

@Bean
public BlockchainDataTransactionDetailsService blockchainDataTransactionDetailsService(CardanoNetwork network, BackendService backendService) {
public BlockchainDataTransactionDetailsService blockchainDataTransactionDetailsService(CardanoNetwork network,
@Qualifier("yaci_blockfrost") BackendService backendService) {
return new BackendServiceBlockchainDataTransactionDetailsService(backendService, network);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.bloxbean.cardano.client.backend.api.BackendService;
import com.bloxbean.cardano.client.backend.blockfrost.service.BFBackendService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
Expand All @@ -12,9 +13,16 @@
public class CardanoClientLibConfig {

@Bean
public BackendService backendService(@Value("${blockfrost.url}") String blockfrostUrl,
@Value("${blockfrost.api.key}") String blockfrostApiKey) {
@Qualifier("original_blockfrost")
public BackendService orgBackendService(@Value("${blockfrost.url}") String blockfrostUrl,
@Value("${blockfrost.api.key}") String blockfrostApiKey) {
return new BFBackendService(blockfrostUrl, blockfrostApiKey);
}

@Bean
@Qualifier("yaci_blockfrost")
public BackendService yaciBackendService() {
return new BFBackendService("http://localhost:9090/yaci-api/", "");
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public class MetadataEventHandler {
@Transactional
public void handleMetadataEvent(TxMetadataEvent event) {
log.debug("Received metadata event: {}", event);

try {
event.getTxMetadataList().stream()
.filter(txMetadataLabel -> txMetadataLabel.getLabel().equalsIgnoreCase(String.valueOf(metadataLabel)))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package org.cardano.foundation.voting.service.chain_sync;

import com.bloxbean.cardano.client.api.exception.ApiException;
import com.bloxbean.cardano.client.backend.blockfrost.service.BFBackendService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.util.Optional;

@Service
@Slf4j
public class ChainSyncService {

@Autowired
@Qualifier("original_blockfrost")
private BFBackendService orgBackendService;

@Autowired
@Qualifier("yaci_blockfrost")
private BFBackendService yaciBackendService;

@Value("${chain.sync.buffer:5}")
private final int chainSyncBuffer = 5;

public SyncStatus getSyncStatus() {
try {
var orgLastBlockResult = orgBackendService.getBlockService().getLatestBlock();
var yaciLastBlockResult = yaciBackendService.getBlockService().getLatestBlock();

if (orgLastBlockResult.isSuccessful() && yaciLastBlockResult.isSuccessful()) {
long diff = orgLastBlockResult.getValue().getSlot() - yaciLastBlockResult.getValue().getSlot();

log.info("Current diff: {} (slots) between org and yaci.", diff);

boolean isSynced = diff >= 0 && diff <= chainSyncBuffer;

if (isSynced) {
return SyncStatus.ok(diff);
}

log.warn("Yaci is not synced with the original chain. Diff: {} (slots)", diff);

return SyncStatus.notYet(diff);
}

return SyncStatus.unknownError();
} catch (ApiException e) {
log.error("Backend service is not available: {}", e.getMessage());

return SyncStatus.error(e);
}
}

public record SyncStatus(boolean isSynced, Optional<Long> diff, Optional<Exception> ex) {

static SyncStatus ok(long diff) {
return new SyncStatus(true, Optional.of(diff), Optional.empty());
}

static SyncStatus notYet(long diff) {
return new SyncStatus(false, Optional.of(diff), Optional.empty());
}

static SyncStatus error(Exception ex) {
return new SyncStatus(false, Optional.empty(), Optional.of(ex));
}

static SyncStatus unknownError() {
return new SyncStatus(false, Optional.empty(), Optional.empty());
}

}

}
Original file line number Diff line number Diff line change
@@ -1,21 +1,44 @@
package org.cardano.foundation.voting.service.health;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.cardano.foundation.voting.service.chain_sync.ChainSyncService;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.stereotype.Component;

@Component
@Component("yaci_store_chain_sync")
@Slf4j
@RequiredArgsConstructor
public class YaciStoreTipHealthIndicator implements HealthIndicator {

private final ChainSyncService chainSyncService;

@Override
public Health health() {
// TODO Yaci-Store integration to check if we have joined the tip
var syncStatus = chainSyncService.getSyncStatus();

if (syncStatus.isSynced()) {
return Health
.up()
.withDetail("message", "Yaci-Store synced with the original chain!")
.withDetail("diffSlots", syncStatus.diff().orElse(-1L))
.build();
}

if (syncStatus.ex().isPresent()) {
return Health
.down()
.withDetail("message", "Yaci-Store error...")
.withDetail("diffSlots", syncStatus.diff().orElse(-1L))
.withException(syncStatus.ex().get())
.build();
}

return Health
.up()
.withDetail("message", "Yaci-Store integration not implemented yet!")
.down()
.withDetail("message", "Yaci-Store is syncing...")
.withDetail("diffSlots", syncStatus.diff().orElse(-1L))
.build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,18 +53,18 @@ store.cardano.port=${CARDANO_NODE_PORT:30000}
store.cardano.protocol-magic=${CARDANO_NODE_PROTOCOL_MAGIC:1}

# CIP-1694 Pre Ratification start block
#store.cardano.sync-start-blockhash=${YACI_STORE_CARDANO_SYNC_START_BLOCK_HASH:ca71d7941c316561c3b21d0f557f34d97a61f97a333c18ed5f2800d74b64e377}
#store.cardano.sync-start-slot=${YACI_STORE_CARDANO_SYNC_START_SLOT:32349482}
store.cardano.sync-start-blockhash=${YACI_STORE_CARDANO_SYNC_START_BLOCK_HASH:ca71d7941c316561c3b21d0f557f34d97a61f97a333c18ed5f2800d74b64e377}
store.cardano.sync-start-slot=${YACI_STORE_CARDANO_SYNC_START_SLOT:32349482}

# CF Summit 2023 start block
store.cardano.sync-start-blockhash=${YACI_STORE_CARDANO_SYNC_START_BLOCK_HASH:69a01168a752b9f0aa3d1cd49d9a87fcffceb6738416c199740933fba002c922}
store.cardano.sync-start-slot=${YACI_STORE_CARDANO_SYNC_START_SLOT:37194728}
#store.cardano.sync-start-blockhash=${YACI_STORE_CARDANO_SYNC_START_BLOCK_HASH:69a01168a752b9f0aa3d1cd49d9a87fcffceb6738416c199740933fba002c922}
#store.cardano.sync-start-slot=${YACI_STORE_CARDANO_SYNC_START_SLOT:37194728}

# 1 day
store.blocks.epoch-calculation-interval=86400

# TODO, how to disable controllers from Yaci
apiPrefix=:${API_PREFIX:/api}
# TODO, how to disable controller`s from Yaci
apiPrefix=${API_PREFIX:/yaci-api}

# default spring profile is a development profile with an external preprod environment
spring.profiles.active=${SPRING_PROFILES_ACTIVE:dev--preprod}
Expand All @@ -73,4 +73,6 @@ cors.allowed.origins=${CORS_ALLOWED_ORIGINS:http://localhost:3000}
# probably disable in production...
spring.h2.console.enabled=${H2_CONSOLE_ENABLED:true}

server.port=9090
chain.sync.buffer=${CHAIN_SYNC_BUFFER:5}

server.port=9090

0 comments on commit 9821d06

Please sign in to comment.