KeyStoreTemplate.java

/*
 * @copyright defined in LICENSE.txt
 */

package hera.client;

import static hera.api.function.Functions.identify;
import static hera.client.ClientConstants.KEYSTORE_CREATE;
import static hera.client.ClientConstants.KEYSTORE_EXPORTKEY;
import static hera.client.ClientConstants.KEYSTORE_IMPORTKEY;
import static hera.client.ClientConstants.KEYSTORE_LIST;
import static hera.client.ClientConstants.KEYSTORE_LOCK;
import static hera.client.ClientConstants.KEYSTORE_SIGN;
import static hera.client.ClientConstants.KEYSTORE_UNLOCK;

import hera.ContextProvider;
import hera.ContextProviderInjectable;
import hera.annotation.ApiAudience;
import hera.annotation.ApiStability;
import hera.api.KeyStoreOperation;
import hera.api.function.Function0;
import hera.api.function.Function1;
import hera.api.function.Function3;
import hera.api.model.AccountAddress;
import hera.api.model.Authentication;
import hera.api.model.EncryptedPrivateKey;
import hera.api.model.RawTransaction;
import hera.api.model.Transaction;
import hera.client.internal.KeyStoreBaseTemplate;
import hera.exception.DecoratorChainException;
import hera.exception.RpcException;
import hera.exception.RpcExceptionConverter;
import hera.strategy.PriorityProvider;
import hera.strategy.StrategyApplier;
import hera.util.ExceptionConverter;
import io.grpc.ManagedChannel;
import io.grpc.Status.Code;
import io.grpc.StatusRuntimeException;
import java.util.List;
import java.util.concurrent.Future;
import lombok.AccessLevel;
import lombok.Getter;

@ApiAudience.Private
@ApiStability.Unstable
public class KeyStoreTemplate
    implements KeyStoreOperation, ChannelInjectable, ContextProviderInjectable {

  protected final ExceptionConverter<RpcException> exceptionConverter = new RpcExceptionConverter();

  protected KeyStoreBaseTemplate keyStoreBaseTemplate = new KeyStoreBaseTemplate();

  protected ContextProvider contextProvider;

  @Getter(lazy = true, value = AccessLevel.PROTECTED)
  private final StrategyApplier strategyApplier =
      StrategyApplier.of(contextProvider.get(), PriorityProvider.get());

  @Override
  public void setChannel(final ManagedChannel channel) {
    this.keyStoreBaseTemplate.setChannel(channel);;
  }

  @Override
  public void setContextProvider(final ContextProvider contextProvider) {
    this.contextProvider = contextProvider;
    this.keyStoreBaseTemplate.setContextProvider(contextProvider);
  }

  @Getter(lazy = true, value = AccessLevel.PROTECTED)
  private final Function0<Future<List<AccountAddress>>> listFunction =
      getStrategyApplier()
          .apply(identify(this.keyStoreBaseTemplate.getListFunction(), KEYSTORE_LIST));

  @Getter(lazy = true, value = AccessLevel.PROTECTED)
  private final Function1<String, Future<AccountAddress>> createFunction =
      getStrategyApplier()
          .apply(identify(this.keyStoreBaseTemplate.getCreateFunction(), KEYSTORE_CREATE));

  @Getter(lazy = true, value = AccessLevel.PROTECTED)
  private final Function1<Authentication, Future<Boolean>> unlockFunction =
      getStrategyApplier()
          .apply(identify(this.keyStoreBaseTemplate.getUnlockFunction(), KEYSTORE_UNLOCK));

  @Getter(lazy = true, value = AccessLevel.PROTECTED)
  private final Function1<Authentication, Future<Boolean>> lockFunction =
      getStrategyApplier()
          .apply(identify(this.keyStoreBaseTemplate.getLockFunction(), KEYSTORE_LOCK));

  @Getter(lazy = true, value = AccessLevel.PROTECTED)
  private final Function1<RawTransaction, Future<Transaction>> signFunction =
      getStrategyApplier()
          .apply(identify(this.keyStoreBaseTemplate.getSignFunction(), KEYSTORE_SIGN));

  @Getter(lazy = true, value = AccessLevel.PROTECTED)
  private final Function3<EncryptedPrivateKey, String, String,
      Future<AccountAddress>> importKeyFunction = getStrategyApplier().apply(
          identify(this.keyStoreBaseTemplate.getImportKeyFunction(), KEYSTORE_IMPORTKEY));

  @Getter(lazy = true, value = AccessLevel.PROTECTED)
  private final Function1<Authentication,
      Future<EncryptedPrivateKey>> exportKeyFunction = getStrategyApplier().apply(
          identify(this.keyStoreBaseTemplate.getExportKeyFunction(), KEYSTORE_EXPORTKEY));

  @Override
  public List<AccountAddress> list() {
    try {
      return getListFunction().apply().get();
    } catch (Exception e) {
      throw exceptionConverter.convert(e);
    }
  }

  @Override
  public AccountAddress create(final String password) {
    try {
      return getCreateFunction().apply(password).get();
    } catch (Exception e) {
      throw exceptionConverter.convert(e);
    }
  }

  @Override
  public boolean lock(final Authentication authentication) {
    try {
      return getLockFunction().apply(authentication).get();
    } catch (DecoratorChainException e) {
      if ((e.getCause() instanceof StatusRuntimeException)
          && ((StatusRuntimeException) e.getCause()).getStatus().getCode().equals(Code.UNKNOWN)) {
        return false;
      } else {
        throw exceptionConverter.convert(e);
      }
    } catch (Exception e) {
      throw exceptionConverter.convert(e);
    }
  }

  @Override
  public boolean unlock(final Authentication authentication) {
    try {
      return getUnlockFunction().apply(authentication).get();
    } catch (DecoratorChainException e) {
      e.printStackTrace();
      if ((e.getCause() instanceof StatusRuntimeException)
          && ((StatusRuntimeException) e.getCause()).getStatus().getCode().equals(Code.UNKNOWN)) {
        return false;
      } else {
        throw exceptionConverter.convert(e);
      }
    } catch (Exception e) {
      throw exceptionConverter.convert(e);
    }
  }

  @Override
  public Transaction sign(final RawTransaction rawTransaction) {
    try {
      return getSignFunction().apply(rawTransaction).get();
    } catch (Exception e) {
      throw exceptionConverter.convert(e);
    }
  }

  @Override
  public AccountAddress importKey(final EncryptedPrivateKey encryptedKey,
      final String oldPassword, final String newPassword) {
    try {
      return getImportKeyFunction().apply(encryptedKey, oldPassword, newPassword).get();
    } catch (Exception e) {
      throw exceptionConverter.convert(e);
    }
  }

  @Override
  public EncryptedPrivateKey exportKey(final Authentication authentication) {
    try {
      return getExportKeyFunction().apply(authentication).get();
    } catch (Exception e) {
      throw exceptionConverter.convert(e);
    }
  }

}