Skip to content

Commit

Permalink
feat: RA-78 Refactor PostgresLedgerDataProviderService and extract bl…
Browse files Browse the repository at this point in the history
…ock* methods to LedgerBlockService
  • Loading branch information
shleger committed Apr 25, 2024
1 parent bcd12ae commit 1e3b9da
Show file tree
Hide file tree
Showing 19 changed files with 346 additions and 288 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import org.cardanofoundation.rosetta.common.util.CardanoAddressUtils;
import org.cardanofoundation.rosetta.common.util.ValidationUtil;
import org.springframework.stereotype.Service;
import org.openapitools.client.model.AccountBalanceRequest;
import org.openapitools.client.model.AccountBalanceResponse;
Expand All @@ -21,9 +19,12 @@
import org.cardanofoundation.rosetta.api.account.service.AccountService;
import org.cardanofoundation.rosetta.api.block.model.domain.Block;
import org.cardanofoundation.rosetta.api.block.model.domain.StakeAddressBalance;
import org.cardanofoundation.rosetta.api.block.service.LedgerBlockService;
import org.cardanofoundation.rosetta.common.exception.ExceptionFactory;
import org.cardanofoundation.rosetta.common.mapper.DataMapper;
import org.cardanofoundation.rosetta.common.services.LedgerDataProviderService;
import org.cardanofoundation.rosetta.common.util.CardanoAddressUtils;
import org.cardanofoundation.rosetta.common.util.ValidationUtil;


@Service
Expand All @@ -32,6 +33,7 @@
public class AccountServiceImpl implements AccountService {

private final LedgerDataProviderService ledgerDataProviderService;
private final LedgerBlockService ledgerBlockService;

@Override
public AccountBalanceResponse getAccountBalance(AccountBalanceRequest accountBalanceRequest) {
Expand Down Expand Up @@ -70,7 +72,7 @@ public AccountCoinsResponse getAccountCoins(AccountCoinsRequest accountCoinsRequ
}
List<Currency> currenciesRequested = ValidationUtil.filterRequestedCurrencies(currencies);
log.debug("[accountCoins] Filter currency is {}", currenciesRequested);
Block latestBlock = ledgerDataProviderService.findLatestBlock();
Block latestBlock = ledgerBlockService.findLatestBlock();
log.debug("[accountCoins] Latest block is {}", latestBlock);
List<Utxo> utxos = ledgerDataProviderService.findUtxoByAddressAndCurrency(accountAddress,
currenciesRequested);
Expand All @@ -82,9 +84,9 @@ private AccountBalanceResponse findBalanceDataByAddressAndBlock(String address,
String hash) {
Block blockDto;
if (number != null || hash != null) {
blockDto = ledgerDataProviderService.findBlock(number, hash);
blockDto = ledgerBlockService.findBlock(number, hash);
} else {
blockDto = ledgerDataProviderService.findLatestBlock();
blockDto = ledgerBlockService.findLatestBlock();
}

if (Objects.isNull(blockDto)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,4 @@ public class Delegation {

private String address;

public static Delegation fromEntity(DelegationEntity entity) {
return Delegation.builder()
.txHash(entity.getTxHash())
.certIndex(entity.getCertIndex())
.poolId(entity.getPoolId())
.address(entity.getAddress())
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,4 @@ public class PoolRetirement {

private Integer epoch;

public static PoolRetirement fromEntity(PoolRetirementEntity entity) {
return PoolRetirement.builder()
.txHash(entity.getTxHash())
.certIndex(entity.getCertIndex())
.poolId(entity.getPoolId())
.epoch(entity.getEpoch())
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,4 @@ public class StakeRegistration {

private String address;

public static StakeRegistration fromEntity(StakeRegistrationEntity entity) {
return StakeRegistration.builder()
.txHash(entity.getTxHash())
.certIndex(entity.getCertIndex())
.type(entity.getType())
.address(entity.getAddress())
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
import org.cardanofoundation.rosetta.api.block.model.domain.BlockTx;
import org.cardanofoundation.rosetta.common.exception.ExceptionFactory;
import org.cardanofoundation.rosetta.common.services.ProtocolParamService;
import org.cardanofoundation.rosetta.common.services.LedgerDataProviderService;

import static java.util.Objects.nonNull;

Expand All @@ -18,16 +17,15 @@
@RequiredArgsConstructor
public class BlockServiceImpl implements BlockService {

private final LedgerDataProviderService ledgerDataProviderService;

private final LedgerBlockService ledgerBlockService;
private final ProtocolParamService protocolParamService;


@Override
public Block findBlock(Long index, String hash) {

log.info("[block] Looking for block: hash={}, index={}", hash, index);
Block block = ledgerDataProviderService.findBlock(index, hash);
Block block = ledgerBlockService.findBlock(index, hash);
if (nonNull(block)) {
log.info("[block] Block was found, hash={}", block.getHash());

Expand All @@ -42,7 +40,7 @@ public Block findBlock(Long index, String hash) {

@Override
public BlockTx getBlockTransaction(Long blockId, String blockHash, String txHash) {
return ledgerDataProviderService
return ledgerBlockService
.findTransactionsByBlock(blockId, blockHash)
.stream()
.filter(tr -> tr.getHash().equals(txHash))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package org.cardanofoundation.rosetta.api.block.service;

import java.util.List;

import org.cardanofoundation.rosetta.api.block.model.domain.Block;
import org.cardanofoundation.rosetta.api.block.model.domain.BlockTx;
import org.cardanofoundation.rosetta.api.block.model.domain.GenesisBlock;

public interface LedgerBlockService {

/**
* Returns a block by its number and hash. Including all populated Transactions.
*
* @param number block number
* @param hash block hash
* @return the block
*/
Block findBlock(Long number, String hash);


/**
* Returns a list of all transactions within a block. The UTXO aren't populated yet. They contain only the hash and the index.
* @param number block number
* @param hash block hash
* @return the list of transactions
*/
List<BlockTx> findTransactionsByBlock(Long number, String hash);


Block findLatestBlock();

GenesisBlock findGenesisBlock();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
package org.cardanofoundation.rosetta.api.block.service;

import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.TimeZone;
import java.util.stream.Collectors;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import org.springframework.stereotype.Component;

import org.apache.commons.lang3.ObjectUtils;
import org.modelmapper.ModelMapper;

import org.cardanofoundation.rosetta.api.account.model.entity.AddressUtxoEntity;
import org.cardanofoundation.rosetta.api.account.model.repository.AddressUtxoRepository;
import org.cardanofoundation.rosetta.api.block.mapper.BlockToEntity;
import org.cardanofoundation.rosetta.api.block.mapper.BlockTxToEntity;
import org.cardanofoundation.rosetta.api.block.mapper.WithdrawalEntityToWithdrawal;
import org.cardanofoundation.rosetta.api.block.model.domain.Block;
import org.cardanofoundation.rosetta.api.block.model.domain.BlockTx;
import org.cardanofoundation.rosetta.api.block.model.domain.Delegation;
import org.cardanofoundation.rosetta.api.block.model.domain.GenesisBlock;
import org.cardanofoundation.rosetta.api.block.model.domain.PoolRegistration;
import org.cardanofoundation.rosetta.api.block.model.domain.PoolRetirement;
import org.cardanofoundation.rosetta.api.block.model.domain.ProtocolParams;
import org.cardanofoundation.rosetta.api.block.model.domain.StakeRegistration;
import org.cardanofoundation.rosetta.api.block.model.entity.BlockEntity;
import org.cardanofoundation.rosetta.api.block.model.entity.TxnEntity;
import org.cardanofoundation.rosetta.api.block.model.repository.BlockRepository;
import org.cardanofoundation.rosetta.api.block.model.repository.DelegationRepository;
import org.cardanofoundation.rosetta.api.block.model.repository.PoolRegistrationRepository;
import org.cardanofoundation.rosetta.api.block.model.repository.PoolRetirementRepository;
import org.cardanofoundation.rosetta.api.block.model.repository.StakeRegistrationRepository;
import org.cardanofoundation.rosetta.api.block.model.repository.TxRepository;
import org.cardanofoundation.rosetta.api.block.model.repository.WithdrawalRepository;
import org.cardanofoundation.rosetta.common.exception.ExceptionFactory;

import org.cardanofoundation.rosetta.api.account.model.domain.Utxo;
import org.cardanofoundation.rosetta.common.services.ProtocolParamService;


@Slf4j
@Component
@RequiredArgsConstructor
public class LedgerBlockServiceImpl implements LedgerBlockService {

private final ProtocolParamService protocolParamService;

private final BlockRepository blockRepository;
private final TxRepository txRepository;
private final StakeRegistrationRepository stakeRegistrationRepository;
private final DelegationRepository delegationRepository;
private final PoolRegistrationRepository poolRegistrationRepository;
private final PoolRetirementRepository poolRetirementRepository;
private final WithdrawalRepository withdrawalRepository;
private final AddressUtxoRepository addressUtxoRepository;


private final ModelMapper mapper;
private final BlockToEntity mapperBlock;
private final BlockTxToEntity mapperTran;
private final WithdrawalEntityToWithdrawal withdrawalEntityToWithdrawal;



@Override
public Block findBlock(Long blockNumber, String blockHash) {
log.debug(
"[findBlock] Parameters received for run query blockNumber: {} , blockHash: {}",
blockNumber, blockHash);
List<BlockEntity> blocks;
if (blockHash == null && blockNumber != null) {
blocks = blockRepository.findByNumber(blockNumber);
} else if (blockHash != null && blockNumber == null) {
blocks = blockRepository.findByHash(blockHash);
} else {
blocks = blockRepository.findByNumberAndHash(blockNumber, blockHash);
}
if (!blocks.isEmpty()) {
log.debug("[findBlock] Block found!");
BlockEntity block = blocks.getFirst();
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
// Populating transactions
Block model = mapperBlock.fromEntity(block);
populateTransactions(model.getTransactions());
return model;
}
log.debug("[findBlock] No block was found");
return null;
}

@Override
public List<BlockTx> findTransactionsByBlock(Long blockNumber, String blockHash) {
log.debug(
"[findTransactionsByBlock] Parameters received for run query blockNumber: {} blockHash: {}",
blockNumber, blockHash);

List<BlockEntity> byNumberAndHash = blockRepository.findByNumberAndHash(blockNumber, blockHash);
if (byNumberAndHash.isEmpty()) {
log.debug(
"[findTransactionsByBlock] No block found for blockNumber: {} blockHash: {}",
blockNumber, blockHash);
return Collections.emptyList();
}
List<TxnEntity> txList = txRepository.findTransactionsByBlockHash(
byNumberAndHash.getFirst().getHash());
log.debug(
"[findTransactionsByBlock] Found {} transactions", txList.size());
if (ObjectUtils.isNotEmpty(txList)) {
List<BlockTx> transactions = txList.stream().map(mapperTran::fromEntity).toList();
populateTransactions(transactions);
return transactions;
}
return Collections.emptyList();
}


@Override
public Block findLatestBlock() {
log.info("[getLatestBlock] About to look for latest block");
Long latestBlockNumber = blockRepository.findLatestBlockNumber();
log.info("[getLatestBlock] Latest block number is {}", latestBlockNumber);
Block latestBlock = findBlock(latestBlockNumber, null);
if (Objects.isNull(latestBlock)) {
log.error("[getLatestBlock] Latest block not found");
throw ExceptionFactory.blockNotFoundException();
}
log.debug("[getLatestBlock] Returning latest block {}", latestBlock);
return latestBlock;
}

@Override
public GenesisBlock findGenesisBlock() {
log.debug("[findGenesisBlock] About to run findGenesisBlock query");
List<BlockEntity> blocks = blockRepository.findGenesisBlock();
if (!blocks.isEmpty()) {
BlockEntity genesis = blocks.getFirst();
return GenesisBlock.builder().hash(genesis.getHash())
.number(genesis.getNumber())
.build();
}
log.debug("[findGenesisBlock] Genesis block was not found");
return null;
}



private void populateTransactions(List<BlockTx> transactions) {
for (BlockTx transaction : transactions) {
populateUtxos(transaction.getInputs());
populateUtxos(transaction.getOutputs());

transaction.setStakeRegistrations(
stakeRegistrationRepository
.findByTxHash(transaction.getHash())
.stream()
.map(m -> mapper.map(m, StakeRegistration.class))
.collect(Collectors.toList()));

transaction.setDelegations(
delegationRepository
.findByTxHash(transaction.getHash())
.stream()
.map(m -> mapper.map(m, Delegation.class))
.collect(Collectors.toList()));

transaction.setPoolRegistrations(poolRegistrationRepository
.findByTxHash(transaction.getHash())
.stream()
.map(PoolRegistration::fromEntity)
.toList()); // TODO Refacotring - do this via JPA

transaction.setPoolRetirements(poolRetirementRepository
.findByTxHash(transaction.getHash())
.stream()
.map(m -> mapper.map(m, PoolRetirement.class))
.collect(Collectors.toList()));

transaction.setWithdrawals(withdrawalRepository
.findByTxHash(transaction.getHash())
.stream()
.map(withdrawalEntityToWithdrawal::fromEntity)
.toList());

ProtocolParams protocolParametersFromIndexerAndConfig = protocolParamService.findProtocolParametersFromIndexerAndConfig();
transaction.setSize((Long.parseLong(transaction.getFee()) - protocolParametersFromIndexerAndConfig.getMinFeeB().longValue()) / protocolParametersFromIndexerAndConfig.getMinFeeA().longValue());
}
}

private void populateUtxos(List<Utxo> inputs) {
for (Utxo utxo : inputs) {
AddressUtxoEntity first = addressUtxoRepository.findAddressUtxoEntitiesByOutputIndexAndTxHash(
utxo.getOutputIndex(), utxo.getTxHash()).getFirst();
if (first != null) {
// Populating the values from entity to model
mapper.map(first, utxo);
}
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import org.cardanofoundation.rosetta.common.services.CardanoService;
import org.cardanofoundation.rosetta.api.construction.service.ConstructionApiService;
import org.cardanofoundation.rosetta.common.services.LedgerDataProviderService;
import org.cardanofoundation.rosetta.common.services.ProtocolParamService;
import org.cardanofoundation.rosetta.common.util.Constants;
import org.cardanofoundation.rosetta.common.util.CborEncodeUtil;
import org.cardanofoundation.rosetta.common.mapper.CborMapToTransactionExtraData;
Expand All @@ -40,7 +41,7 @@ public class ConstructionApiServiceImpl implements ConstructionApiService {

private final CardanoAddressService cardanoAddressService;
private final CardanoService cardanoService;
private final LedgerDataProviderService ledgerService;
private final ProtocolParamService protocolParamService;
private final DataMapper dataMapper;

@Override
Expand Down Expand Up @@ -108,7 +109,8 @@ public ConstructionMetadataResponse constructionMetadataService(
log.debug("[constructionMetadata] updating tx size from {}", txSize);
Long updatedTxSize = cardanoService.updateTxSize(txSize.longValue(), 0L, ttl);
log.debug("[constructionMetadata] updated txSize size is ${updatedTxSize}");
ProtocolParams protocolParams = ledgerService.findProtocolParametersFromIndexerAndConfig();
ProtocolParams protocolParams =
protocolParamService.findProtocolParametersFromIndexerAndConfig();
log.debug("[constructionMetadata] received protocol parameters from block-service {}",
protocolParams);
Long suggestedFee = cardanoService.calculateTxMinimumFee(updatedTxSize, protocolParams);
Expand Down

0 comments on commit 1e3b9da

Please sign in to comment.