KeyStoreBaseTemplate.java
/*
* @copyright defined in LICENSE.txt
*/
package hera.client.internal;
import static hera.util.TransportUtils.sha256AndEncodeHexa;
import static org.slf4j.LoggerFactory.getLogger;
import static types.AergoRPCServiceGrpc.newFutureStub;
import hera.ContextProvider;
import hera.ContextProviderInjectable;
import hera.annotation.ApiAudience;
import hera.annotation.ApiStability;
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.ChannelInjectable;
import hera.transport.AccountAddressConverterFactory;
import hera.transport.AuthenticationConverterFactory;
import hera.transport.EncryptedPrivateKeyConverterFactory;
import hera.transport.ModelConverter;
import hera.transport.TransactionConverterFactory;
import io.grpc.ManagedChannel;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Future;
import lombok.Getter;
import org.slf4j.Logger;
import types.AccountOuterClass;
import types.AergoRPCServiceGrpc.AergoRPCServiceFutureStub;
import types.Blockchain;
import types.Rpc;
@ApiAudience.Private
@ApiStability.Unstable
public class KeyStoreBaseTemplate implements ChannelInjectable, ContextProviderInjectable {
protected final transient Logger logger = getLogger(getClass());
protected final ModelConverter<EncryptedPrivateKey, Rpc.SingleBytes> encryptedPkConverter =
new EncryptedPrivateKeyConverterFactory().create();
protected final ModelConverter<AccountAddress,
com.google.protobuf.ByteString> accountAddressConverter =
new AccountAddressConverterFactory().create();
protected final ModelConverter<Authentication, Rpc.Personal> authenticationConverter =
new AuthenticationConverterFactory().create();
protected final ModelConverter<Transaction, Blockchain.Tx> transactionConverter =
new TransactionConverterFactory().create();
@Getter
protected AergoRPCServiceFutureStub aergoService;
protected ContextProvider contextProvider;
@Override
public void setChannel(final ManagedChannel channel) {
this.aergoService = newFutureStub(channel);
}
@Override
public void setContextProvider(final ContextProvider contextProvider) {
this.contextProvider = contextProvider;
}
@Getter
private final Function0<
Future<List<AccountAddress>>> listFunction = new Function0<Future<List<AccountAddress>>>() {
@Override
public Future<List<AccountAddress>> apply() {
logger.debug("List keystore addresses");
final Rpc.Empty empty = Rpc.Empty.newBuilder().build();
logger.trace("AergoService getAccounts arg: {}", empty);
final Future<AccountOuterClass.AccountList> rawFuture = aergoService.getAccounts(empty);
final Future<List<AccountAddress>> convertedFuture = HerajFutures.transform(rawFuture,
new Function1<AccountOuterClass.AccountList, List<AccountAddress>>() {
@Override
public List<AccountAddress> apply(
final AccountOuterClass.AccountList rpcAccountList) {
final List<AccountAddress> domainAccountList = new ArrayList<>();
for (final AccountOuterClass.Account rpcAccount : rpcAccountList
.getAccountsList()) {
final AccountAddress domainAccount =
accountAddressConverter.convertToDomainModel(rpcAccount.getAddress());
domainAccountList.add(domainAccount);
}
return domainAccountList;
}
});
return convertedFuture;
}
};
@Getter
private final Function1<String,
Future<AccountAddress>> createFunction = new Function1<String, Future<AccountAddress>>() {
@Override
public Future<AccountAddress> apply(final String password) {
if (logger.isDebugEnabled()) {
logger.debug("Create an account to server keystore with password: {}",
sha256AndEncodeHexa(password));
}
final Rpc.Personal rpcPassword =
Rpc.Personal.newBuilder().setPassphrase(password).build();
if (logger.isTraceEnabled()) {
logger.trace("AergoService createAccount arg: {}",
sha256AndEncodeHexa(rpcPassword.getPassphrase()));
}
final Future<AccountOuterClass.Account> rawFuture =
aergoService.createAccount(rpcPassword);
final Future<AccountAddress> convertedFuture = HerajFutures.transform(rawFuture,
new Function1<AccountOuterClass.Account, AccountAddress>() {
@Override
public AccountAddress apply(final AccountOuterClass.Account rpcAccount) {
return accountAddressConverter.convertToDomainModel(rpcAccount.getAddress());
}
});
return convertedFuture;
}
};
@Getter
private final Function1<Authentication,
Future<Boolean>> unlockFunction = new Function1<Authentication, Future<Boolean>>() {
@Override
public Future<Boolean> apply(final Authentication authentication) {
logger.debug("Unlock an account in server keystore with authentication: {}",
authentication);
final Rpc.Personal rpcAuthentication =
authenticationConverter.convertToRpcModel(authentication);
if (logger.isTraceEnabled()) {
logger.trace("AergoService unlockAccount arg: {}, {}",
rpcAuthentication.getAccount(),
sha256AndEncodeHexa(rpcAuthentication.getPassphrase()));
}
final Future<AccountOuterClass.Account> rawFuture =
aergoService.unlockAccount(rpcAuthentication);
final Future<Boolean> convertedFuture = HerajFutures.transform(rawFuture,
new Function1<AccountOuterClass.Account, Boolean>() {
@Override
public Boolean apply(final AccountOuterClass.Account rpcAccount) {
return null != rpcAccount.getAddress();
}
});
return convertedFuture;
}
};
@Getter
private final Function1<Authentication, Future<Boolean>> lockFunction =
new Function1<Authentication, Future<Boolean>>() {
@Override
public Future<Boolean> apply(final Authentication authentication) {
logger.debug("Lock an account in server keystore with authentication: {}",
authentication);
final Rpc.Personal rpcAuthentication =
authenticationConverter.convertToRpcModel(authentication);
if (logger.isTraceEnabled()) {
logger.trace("AergoService lockAccount arg: {}",
sha256AndEncodeHexa(rpcAuthentication.getPassphrase()));
}
final Future<AccountOuterClass.Account> rawFuture =
aergoService.lockAccount(rpcAuthentication);
final Future<Boolean> convertedFuture = HerajFutures.transform(rawFuture,
new Function1<AccountOuterClass.Account, Boolean>() {
@Override
public Boolean apply(final AccountOuterClass.Account rpcAccount) {
return null != rpcAccount.getAddress();
}
});
return convertedFuture;
}
};
@Getter
private final Function1<RawTransaction,
Future<Transaction>> signFunction = new Function1<RawTransaction, Future<Transaction>>() {
@Override
public Future<Transaction> apply(final RawTransaction rawTransaction) {
logger.debug("Sign request with rawTx: {}", rawTransaction);
final Transaction domainTransaction =
Transaction.newBuilder().rawTransaction(rawTransaction).build();
final Blockchain.Tx rpcTx = transactionConverter.convertToRpcModel(domainTransaction);
logger.trace("AergoService signTX arg: {}", rpcTx);
final Future<Blockchain.Tx> rawFuture = aergoService.signTX(rpcTx);
final Future<Transaction> convertedFuture =
HerajFutures.transform(rawFuture, new Function1<Blockchain.Tx, Transaction>() {
@Override
public Transaction apply(final Blockchain.Tx tx) {
return transactionConverter.convertToDomainModel(tx);
}
});
return convertedFuture;
}
};
@Getter
private final Function3<EncryptedPrivateKey, String, String,
Future<AccountAddress>> importKeyFunction = new Function3<EncryptedPrivateKey, String, String,
Future<AccountAddress>>() {
@Override
public Future<AccountAddress> apply(final EncryptedPrivateKey encryptedKey,
final String oldPassword, final String newPassword) {
if (logger.isDebugEnabled()) {
logger.debug(
"Import an account to server keystore with "
+ "encryptedKey: {}, oldPassword: {}, newPassword: {}",
encryptedKey, sha256AndEncodeHexa(oldPassword),
sha256AndEncodeHexa(newPassword));
}
final Rpc.ImportFormat rpcImport = Rpc.ImportFormat.newBuilder()
.setWif(encryptedPkConverter.convertToRpcModel(encryptedKey))
.setOldpass(oldPassword).setNewpass(newPassword).build();
if (logger.isTraceEnabled()) {
logger.trace(
"AergoService importAccount arg: ImportFormat(wif={}, oldPass={}, newPass={})",
rpcImport.getWif(), sha256AndEncodeHexa(rpcImport.getOldpass()),
sha256AndEncodeHexa(rpcImport.getNewpass()));
}
final Future<AccountOuterClass.Account> rawFuture =
aergoService.importAccount(rpcImport);
final Future<AccountAddress> convertedFuture = HerajFutures.transform(rawFuture,
new Function1<AccountOuterClass.Account, AccountAddress>() {
@Override
public AccountAddress apply(final AccountOuterClass.Account rpcAccount) {
return accountAddressConverter
.convertToDomainModel(rpcAccount.getAddress());
}
});
return convertedFuture;
}
};
@Getter
private final Function1<Authentication, Future<EncryptedPrivateKey>> exportKeyFunction =
new Function1<Authentication, Future<EncryptedPrivateKey>>() {
@Override
public Future<EncryptedPrivateKey> apply(final Authentication authentication) {
logger.debug("Export an account from server keystore with authentication: {}",
authentication);
final Rpc.Personal rpcAuthentication =
authenticationConverter.convertToRpcModel(authentication);
if (logger.isTraceEnabled()) {
logger.trace("AergoService exportAccount arg: Personal(account={}, password={})",
rpcAuthentication.getAccount().getAddress(),
sha256AndEncodeHexa(rpcAuthentication.getPassphrase()));
}
final Future<Rpc.SingleBytes> rawFuture =
aergoService.exportAccount(rpcAuthentication);
final Future<EncryptedPrivateKey> convertedFuture = HerajFutures.transform(rawFuture,
new Function1<Rpc.SingleBytes, EncryptedPrivateKey>() {
@Override
public EncryptedPrivateKey apply(final Rpc.SingleBytes rawPk) {
return encryptedPkConverter.convertToDomainModel(rawPk);
}
});
return convertedFuture;
}
};
}