AccountBaseTemplate.java

  1. /*
  2.  * @copyright defined in LICENSE.txt
  3.  */

  4. package hera.client.internal;

  5. import static hera.util.ValidationUtils.assertTrue;
  6. import static org.slf4j.LoggerFactory.getLogger;
  7. import static types.AergoRPCServiceGrpc.newFutureStub;

  8. import com.google.protobuf.ByteString;
  9. import hera.ContextProvider;
  10. import hera.ContextProviderInjectable;
  11. import hera.annotation.ApiAudience;
  12. import hera.annotation.ApiStability;
  13. import hera.api.function.Function1;
  14. import hera.api.function.Function2;
  15. import hera.api.function.Function3;
  16. import hera.api.function.Function4;
  17. import hera.api.model.Account;
  18. import hera.api.model.AccountAddress;
  19. import hera.api.model.AccountState;
  20. import hera.api.model.AccountTotalVote;
  21. import hera.api.model.Aer;
  22. import hera.api.model.BytesValue;
  23. import hera.api.model.ElectedCandidate;
  24. import hera.api.model.RawTransaction;
  25. import hera.api.model.StakeInfo;
  26. import hera.api.model.Transaction;
  27. import hera.api.model.TxHash;
  28. import hera.client.ChannelInjectable;
  29. import hera.exception.TransactionVerificationException;
  30. import hera.key.Signer;
  31. import hera.key.TxSigner;
  32. import hera.transport.AccountAddressConverterFactory;
  33. import hera.transport.AccountStateConverterFactory;
  34. import hera.transport.AccountTotalVoteConverterFactory;
  35. import hera.transport.ElectedCandidateConverterFactory;
  36. import hera.transport.ModelConverter;
  37. import hera.transport.StakeInfoConverterFactory;
  38. import hera.transport.TransactionConverterFactory;
  39. import io.grpc.ManagedChannel;
  40. import java.util.ArrayList;
  41. import java.util.List;
  42. import java.util.concurrent.Future;
  43. import lombok.Getter;
  44. import org.slf4j.Logger;
  45. import types.AergoRPCServiceGrpc.AergoRPCServiceFutureStub;
  46. import types.Blockchain;
  47. import types.Rpc;

  48. @ApiAudience.Private
  49. @ApiStability.Unstable
  50. public class AccountBaseTemplate implements ChannelInjectable, ContextProviderInjectable {

  51.   protected final transient Logger logger = getLogger(getClass());

  52.   protected final ModelConverter<AccountAddress, ByteString> accountAddressConverter =
  53.       new AccountAddressConverterFactory().create();

  54.   protected final ModelConverter<AccountState, Blockchain.State> accountStateConverter =
  55.       new AccountStateConverterFactory().create();

  56.   protected final ModelConverter<StakeInfo, Rpc.Staking> stakingInfoConverter =
  57.       new StakeInfoConverterFactory().create();

  58.   protected final ModelConverter<Transaction, Blockchain.Tx> transactionConverter =
  59.       new TransactionConverterFactory().create();

  60.   protected final ModelConverter<ElectedCandidate, Rpc.Vote> electedCandidateConverter =
  61.       new ElectedCandidateConverterFactory().create();

  62.   protected final ModelConverter<AccountTotalVote, Rpc.AccountVoteInfo> accountTotalVoteConverter =
  63.       new AccountTotalVoteConverterFactory().create();

  64.   @Getter
  65.   protected AergoRPCServiceFutureStub aergoService;

  66.   protected ContextProvider contextProvider;

  67.   protected TransactionBaseTemplate transactionBaseTemplate = new TransactionBaseTemplate();

  68.   @Override
  69.   public void setContextProvider(final ContextProvider contextProvider) {
  70.     this.contextProvider = contextProvider;
  71.     this.transactionBaseTemplate.setContextProvider(contextProvider);
  72.   }

  73.   @Override
  74.   public void setChannel(final ManagedChannel channel) {
  75.     this.aergoService = newFutureStub(channel);
  76.     this.transactionBaseTemplate.setChannel(channel);
  77.   }

  78.   @Getter
  79.   private final Function1<AccountAddress, Future<AccountState>> stateFunction =
  80.       new Function1<AccountAddress, Future<AccountState>>() {

  81.         @Override
  82.         public Future<AccountState> apply(final AccountAddress address) {
  83.           logger.debug("GetState with {}", address);

  84.           final Rpc.SingleBytes rpcAddress = Rpc.SingleBytes.newBuilder()
  85.               .setValue(accountAddressConverter.convertToRpcModel(address))
  86.               .build();
  87.           logger.trace("AergoService getstate arg: {}", rpcAddress);

  88.           final Future<Blockchain.State> rawFuture = aergoService.getState(rpcAddress);
  89.           final Future<AccountState> convertedFuture =
  90.               HerajFutures.transform(rawFuture, new Function1<Blockchain.State, AccountState>() {

  91.                 @Override
  92.                 public AccountState apply(final Blockchain.State state) {
  93.                   final AccountState withoutAddress =
  94.                       accountStateConverter.convertToDomainModel(state);
  95.                   return AccountState.newBuilder().address(address)
  96.                       .nonce(withoutAddress.getNonce())
  97.                       .balance(withoutAddress.getBalance())
  98.                       .build();
  99.                 }
  100.               });
  101.           return convertedFuture;
  102.         }
  103.       };

  104.   @Getter
  105.   private final Function3<Signer, String, Long, Future<TxHash>> createNameFunction =
  106.       new Function3<Signer, String, Long, Future<TxHash>>() {

  107.         @Override
  108.         public Future<TxHash> apply(final Signer signer, final String name,
  109.             final Long nonce) {
  110.           logger.debug("Create account name with signer: {}, name: {}, nonce: {}",
  111.               signer.getPrincipal(), name, nonce);

  112.           final RawTransaction rawTransaction = RawTransaction.newCreateNameTxBuilder()
  113.               .chainIdHash(contextProvider.get().getChainIdHash())
  114.               .from(signer.getPrincipal())
  115.               .nonce(nonce)
  116.               .name(name)
  117.               .build();
  118.           final Transaction signed = signer.sign(rawTransaction);
  119.           return transactionBaseTemplate.getCommitFunction().apply(signed);
  120.         }
  121.       };

  122.   @Getter
  123.   private final Function4<Signer, String, AccountAddress, Long,
  124.       Future<TxHash>> updateNameFunction = new Function4<Signer, String,
  125.           AccountAddress, Long, Future<TxHash>>() {

  126.         @Override
  127.         public Future<TxHash> apply(final Signer signer, final String name,
  128.             final AccountAddress newOwner, final Long nonce) {
  129.           logger.debug("Update account name with signer: {}, name: {}, to account: {}, nonce: {}",
  130.               signer, name, newOwner, nonce);

  131.           final RawTransaction rawTransaction = RawTransaction.newUpdateNameTxBuilder()
  132.               .chainIdHash(contextProvider.get().getChainIdHash())
  133.               .from(signer.getPrincipal())
  134.               .nonce(nonce)
  135.               .name(name)
  136.               .newOwner(newOwner)
  137.               .build();
  138.           final Transaction signed = signer.sign(rawTransaction);
  139.           return transactionBaseTemplate.getCommitFunction().apply(signed);
  140.         }
  141.       };

  142.   @Getter
  143.   private final Function2<String, Long, Future<AccountAddress>> getNameOwnerFunction =
  144.       new Function2<String, Long, Future<AccountAddress>>() {

  145.         @Override
  146.         public Future<AccountAddress> apply(final String name, final Long blockNumber) {
  147.           logger.debug("Get name owner of name: {}, blockNumber: {}", name, blockNumber);
  148.           assertTrue(blockNumber >= 0, "Block number must >= 0");

  149.           final Rpc.Name rpcName = Rpc.Name.newBuilder()
  150.               .setName(name)
  151.               .setBlockNo(blockNumber)
  152.               .build();
  153.           logger.trace("AergoService getNameInfo arg: {}", rpcName);

  154.           final Future<Rpc.NameInfo> rawFuture = aergoService.getNameInfo(rpcName);
  155.           final Future<AccountAddress> convertedFuture =
  156.               HerajFutures.transform(rawFuture, new Function1<Rpc.NameInfo, AccountAddress>() {

  157.                 @Override
  158.                 public AccountAddress apply(final Rpc.NameInfo nameInfo) {
  159.                   final AccountAddress converted =
  160.                       accountAddressConverter.convertToDomainModel(nameInfo.getOwner());
  161.                   return BytesValue.EMPTY.equals(converted.getBytesValue()) ? null : converted;
  162.                 }
  163.               });
  164.           return convertedFuture;
  165.         }
  166.       };

  167.   @Getter
  168.   private final Function3<Signer, Aer, Long, Future<TxHash>> stakingFunction =
  169.       new Function3<Signer, Aer, Long, Future<TxHash>>() {

  170.         @Override
  171.         public Future<TxHash> apply(final Signer signer, final Aer amount,
  172.             final Long nonce) {
  173.           logger.debug("Staking account with signer: {}, amount: {}, nonce: {}",
  174.               signer.getPrincipal(), amount, nonce);

  175.           final RawTransaction rawTransaction = RawTransaction.newStakeTxBuilder()
  176.               .chainIdHash(contextProvider.get().getChainIdHash())
  177.               .from(signer.getPrincipal())
  178.               .amount(amount)
  179.               .nonce(nonce)
  180.               .build();
  181.           final Transaction signed = signer.sign(rawTransaction);
  182.           return transactionBaseTemplate.getCommitFunction().apply(signed);
  183.         }
  184.       };

  185.   @Getter
  186.   private final Function3<Signer, Aer, Long, Future<TxHash>> unstakingFunction =
  187.       new Function3<Signer, Aer, Long, Future<TxHash>>() {

  188.         @Override
  189.         public Future<TxHash> apply(final Signer signer, final Aer amount,
  190.             final Long nonce) {
  191.           logger.debug("Unstaking account with signer: {}, amount: {}, nonce: {}",
  192.               signer.getPrincipal(), amount, nonce);

  193.           final RawTransaction rawTransaction = RawTransaction.newUnstakeTxBuilder()
  194.               .chainIdHash(contextProvider.get().getChainIdHash())
  195.               .from(signer.getPrincipal())
  196.               .amount(amount)
  197.               .nonce(nonce)
  198.               .build();
  199.           final Transaction signed = signer.sign(rawTransaction);
  200.           return transactionBaseTemplate.getCommitFunction().apply(signed);
  201.         }
  202.       };

  203.   @Getter
  204.   private final Function1<AccountAddress, Future<StakeInfo>> stakingInfoFunction =
  205.       new Function1<AccountAddress, Future<StakeInfo>>() {

  206.         @Override
  207.         public Future<StakeInfo> apply(final AccountAddress accountAddress) {
  208.           logger.debug("Get staking information with address: {}", accountAddress);

  209.           final Rpc.AccountAddress rpcAddress = Rpc.AccountAddress.newBuilder()
  210.               .setValue(accountAddressConverter.convertToRpcModel(accountAddress))
  211.               .build();
  212.           logger.trace("AergoService getStaking arg: {}", rpcAddress);

  213.           final Future<Rpc.Staking> rawFuture = aergoService.getStaking(rpcAddress);
  214.           final Future<StakeInfo> convertedFuture =
  215.               HerajFutures.transform(rawFuture, new Function1<Rpc.Staking, StakeInfo>() {

  216.                 @Override
  217.                 public StakeInfo apply(final Rpc.Staking rpcStaking) {
  218.                   final StakeInfo withoutAddress =
  219.                       stakingInfoConverter.convertToDomainModel(rpcStaking);
  220.                   return StakeInfo.newBuilder().address(accountAddress)
  221.                       .amount(withoutAddress.getAmount())
  222.                       .blockNumber(withoutAddress.getBlockNumber())
  223.                       .build();
  224.                 }
  225.               });
  226.           return convertedFuture;
  227.         }
  228.       };

  229.   @Getter
  230.   private final Function2<Account, RawTransaction, Future<Transaction>> signFunction =
  231.       new Function2<Account, RawTransaction, Future<Transaction>>() {

  232.         @Override
  233.         public Future<Transaction> apply(final Account account,
  234.             final RawTransaction rawTransaction) {
  235.           logger.debug("Sign request with account: {}, rawTx: {}", account.getAddress(),
  236.               rawTransaction);

  237.           if (account instanceof TxSigner) {
  238.             final TxSigner signer = (TxSigner) account;
  239.             final Transaction signed = signer.sign(rawTransaction);
  240.             return HerajFutures.success(signed);
  241.           } else {
  242.             final Transaction domainTransaction = Transaction.newBuilder()
  243.                 .rawTransaction(rawTransaction)
  244.                 .build();
  245.             final Blockchain.Tx rpcTx = transactionConverter.convertToRpcModel(domainTransaction);
  246.             logger.trace("AergoService signTX arg: {}", rpcTx);

  247.             final Future<Blockchain.Tx> rawFuture = aergoService.signTX(rpcTx);
  248.             final Future<Transaction> convertedFuture =
  249.                 HerajFutures.transform(rawFuture, new Function1<Blockchain.Tx, Transaction>() {

  250.                   @Override
  251.                   public Transaction apply(final Blockchain.Tx rpcTx) {
  252.                     return transactionConverter.convertToDomainModel(rpcTx);
  253.                   }
  254.                 });
  255.             return convertedFuture;
  256.           }
  257.         }
  258.       };

  259.   @Getter
  260.   private final Function2<Account, Transaction, Future<Boolean>> verifyFunction =
  261.       new Function2<Account, Transaction, Future<Boolean>>() {

  262.         @Override
  263.         public Future<Boolean> apply(final Account account,
  264.             final Transaction transaction) {
  265.           logger.debug("Sign verify with account: {}, signedTx: {}", account, transaction);

  266.           final Blockchain.Tx rpcTx = transactionConverter.convertToRpcModel(transaction);
  267.           logger.trace("AergoService verifyTX arg: {}", rpcTx);

  268.           final Future<Rpc.VerifyResult> rawFuture = aergoService.verifyTX(rpcTx);
  269.           final Future<Boolean> convertedFuture =
  270.               HerajFutures.transform(rawFuture, new Function1<Rpc.VerifyResult, Boolean>() {

  271.                 @Override
  272.                 public Boolean apply(final Rpc.VerifyResult result) {
  273.                   if (Rpc.VerifyStatus.VERIFY_STATUS_OK != result.getError()) {
  274.                     new TransactionVerificationException(result.getError());
  275.                   }
  276.                   return true;
  277.                 }
  278.               });
  279.           return convertedFuture;
  280.         }
  281.       };

  282.   @Getter
  283.   private final Function4<Signer, String, List<String>, Long,
  284.       Future<TxHash>> voteFunction = new Function4<
  285.           Signer, String, List<String>, Long, Future<TxHash>>() {

  286.         @Override
  287.         public Future<TxHash> apply(final Signer signer, final String voteId,
  288.             final List<String> candidates, final Long nonce) {
  289.           logger.debug("Voting with signer: }, voteId: {}, candidates: {}, nonce: {}",
  290.               signer.getPrincipal(), voteId, candidates, nonce);

  291.           final RawTransaction rawTransaction = RawTransaction.newVoteTxBuilder()
  292.               .chainIdHash(contextProvider.get().getChainIdHash())
  293.               .from(signer.getPrincipal())
  294.               .nonce(nonce)
  295.               .voteId(voteId)
  296.               .candidates(candidates)
  297.               .build();
  298.           final Transaction signed = signer.sign(rawTransaction);
  299.           return transactionBaseTemplate.getCommitFunction().apply(signed);
  300.         }
  301.       };

  302.   @Getter
  303.   private final Function2<String, Integer,
  304.       Future<List<ElectedCandidate>>> listElectedFunction = new Function2<
  305.           String, Integer, Future<List<ElectedCandidate>>>() {

  306.         @Override
  307.         public Future<List<ElectedCandidate>> apply(final String voteId,
  308.             final Integer showCount) {
  309.           logger.debug("Get votes status with voteId: {}, showCount: {}", voteId, showCount);

  310.           final Rpc.VoteParams rpcVoteParams = Rpc.VoteParams.newBuilder()
  311.               .setId(voteId)
  312.               .setCount(showCount)
  313.               .build();
  314.           logger.trace("AergoService getVotes arg: {}", rpcVoteParams);

  315.           final Future<Rpc.VoteList> rawFuture = aergoService.getVotes(rpcVoteParams);
  316.           final Future<List<ElectedCandidate>> convertedFuture =
  317.               HerajFutures.transform(rawFuture,
  318.                   new Function1<Rpc.VoteList, List<ElectedCandidate>>() {

  319.                     @Override
  320.                     public List<ElectedCandidate> apply(final Rpc.VoteList rpcVoteList) {
  321.                       final List<ElectedCandidate> electedCandidates = new ArrayList<>();
  322.                       for (final Rpc.Vote rpcCandidate : rpcVoteList.getVotesList()) {
  323.                         final ElectedCandidate domainElectedCandidate =
  324.                             electedCandidateConverter.convertToDomainModel(rpcCandidate);
  325.                         electedCandidates.add(domainElectedCandidate);
  326.                       }
  327.                       return electedCandidates;
  328.                     }
  329.                   });
  330.           return convertedFuture;
  331.         }
  332.       };

  333.   @Getter
  334.   private final Function1<AccountAddress, Future<AccountTotalVote>> votesOfFunction =
  335.       new Function1<AccountAddress, Future<AccountTotalVote>>() {

  336.         @Override
  337.         public Future<AccountTotalVote> apply(final AccountAddress accountAddress) {
  338.           logger.debug("Get votes with address: {}", accountAddress);

  339.           final Rpc.AccountAddress rpcAddress = Rpc.AccountAddress.newBuilder()
  340.               .setValue(accountAddressConverter.convertToRpcModel(accountAddress))
  341.               .build();
  342.           logger.trace("AergoService getAccountVotes arg: {}", rpcAddress);

  343.           final Future<Rpc.AccountVoteInfo> rawFuture =
  344.               aergoService.getAccountVotes(rpcAddress);
  345.           final Future<AccountTotalVote> convertedFuture =
  346.               HerajFutures.transform(rawFuture,
  347.                   new Function1<Rpc.AccountVoteInfo, AccountTotalVote>() {

  348.                     @Override
  349.                     public AccountTotalVote apply(final Rpc.AccountVoteInfo rpcAccountVoteTotal) {
  350.                       return accountTotalVoteConverter.convertToDomainModel(rpcAccountVoteTotal);
  351.                     }
  352.                   });
  353.           return convertedFuture;
  354.         }
  355.       };

  356. }