package io.emax.cosigner.validator;
import io.emax.cosigner.api.core.CurrencyPackageInterface;
import io.emax.cosigner.api.currency.Wallet;
import io.emax.cosigner.api.currency.Wallet.TransactionDetails;
import io.emax.cosigner.api.validation.Validatable;
import io.emax.cosigner.api.validation.Validator;
import io.emax.cosigner.api.validation.ValidatorConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.math.BigDecimal;
import java.time.Clock;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
public class BasicValidator implements Validator {
private static final Logger LOGGER = LoggerFactory.getLogger(BasicValidator.class);
@Override
public boolean validateTransaction(CurrencyPackageInterface currency, String transaction) {
LOGGER.debug("currency: " + currency.toString());
LOGGER.debug("currency Wallet: " + currency.getWallet());
LOGGER.debug("currency Wallet Class: " + currency.getWallet().getClass());
if (!Validatable.class.isAssignableFrom(currency.getWallet().getClass())) {
// Wallet can not be validated.
LOGGER.debug("Tried validating: " + currency.getConfiguration().getCurrencySymbol()
+ ", was not Validatable.");
return true;
}
if (!ValidatorConfiguration.class.isAssignableFrom(currency.getConfiguration().getClass())) {
// Configuration is not set up, we can't validate.
LOGGER.debug("Tried validating: " + currency.getConfiguration().getCurrencySymbol()
+ ", was not Configured.");
return true;
}
LOGGER.debug("Attempting to validate: " + currency.getConfiguration().getCurrencySymbol());
Wallet wallet = currency.getWallet();
Validatable validatableWallet = (Validatable) currency.getWallet();
ValidatorConfiguration validatorConfig = (ValidatorConfiguration) currency.getConfiguration();
TransactionDetails txDetail = validatableWallet.decodeRawTransaction(transaction);
if (validatorConfig.getMaxAmountPerTransaction().compareTo(BigDecimal.ZERO) != 0
&& txDetail.getAmount().compareTo(validatorConfig.getMaxAmountPerTransaction()) > 0) {
LOGGER.info("Transaction value too high for: " + txDetail.toString());
return false;
}
// Build timed totals.
BigDecimal hourlyTotal = BigDecimal.ZERO;
BigDecimal dailyTotal = BigDecimal.ZERO;
Instant oneHourAgo = Clock.systemUTC().instant().minus(1, ChronoUnit.HOURS);
Instant oneDayAgo = Clock.systemUTC().instant().minus(1, ChronoUnit.DAYS);
for (String senders : txDetail.getFromAddress()) {
TransactionDetails[] txs = wallet.getTransactions(senders, 100, 0);
for (TransactionDetails tx : txs) {
if (tx.getTxDate().toInstant().isAfter(oneHourAgo)) {
hourlyTotal = hourlyTotal.add(tx.getAmount());
}
if (tx.getTxDate().toInstant().isAfter(oneDayAgo)) {
dailyTotal = dailyTotal.add(tx.getAmount());
}
}
}
// Verify that the tx + timed totals are within bounds.
BigDecimal maxPerHour = validatorConfig.getMaxAmountPerHour();
hourlyTotal = hourlyTotal.add(txDetail.getAmount());
if (maxPerHour.compareTo(BigDecimal.ZERO) != 0 && hourlyTotal.compareTo(maxPerHour) > 0) {
LOGGER.info("Transaction value exceeds hourly limit.");
return false;
}
if (validatorConfig.getMaxAmountPerDay().compareTo(BigDecimal.ZERO) != 0
&& dailyTotal.add(txDetail.getAmount()).compareTo(validatorConfig.getMaxAmountPerDay())
> 0) {
LOGGER.info("Transaction value exceeds daily limit.");
return false;
}
// Nothing failed, so it's ok.
LOGGER.debug("Validation passed");
return true;
}
}