CasperDeployService.java

package com.syntifi.casper.sdk.service;

import com.syntifi.casper.sdk.exception.CLValueEncodeException;
import com.syntifi.casper.sdk.exception.DynamicInstanceException;
import com.syntifi.casper.sdk.exception.NoSuchTypeException;
import com.syntifi.casper.sdk.model.clvalue.CLValueOption;
import com.syntifi.casper.sdk.model.clvalue.CLValuePublicKey;
import com.syntifi.casper.sdk.model.clvalue.CLValueU512;
import com.syntifi.casper.sdk.model.clvalue.CLValueU64;
import com.syntifi.casper.sdk.model.clvalue.cltype.CLTypeOption;
import com.syntifi.casper.sdk.model.clvalue.cltype.CLTypePublicKey;
import com.syntifi.casper.sdk.model.clvalue.cltype.CLTypeU512;
import com.syntifi.casper.sdk.model.clvalue.encdec.CLValueEncoder;
import com.syntifi.casper.sdk.model.common.Digest;
import com.syntifi.casper.sdk.model.common.Ttl;
import com.syntifi.casper.sdk.model.deploy.Approval;
import com.syntifi.casper.sdk.model.deploy.Deploy;
import com.syntifi.casper.sdk.model.deploy.DeployHeader;
import com.syntifi.casper.sdk.model.deploy.NamedArg;
import com.syntifi.casper.sdk.model.deploy.executabledeploy.ModuleBytes;
import com.syntifi.casper.sdk.model.deploy.executabledeploy.Transfer;
import com.syntifi.casper.sdk.model.key.PublicKey;
import com.syntifi.casper.sdk.model.key.Signature;
import com.syntifi.crypto.key.AbstractPrivateKey;
import com.syntifi.crypto.key.hash.Blake2b;

import java.io.IOException;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
import java.util.Random;

/**
 * Deploy Service class implementing the process to generate deploys
 *
 * @author Alexandre Carvalho
 * @author Andre Bertolace
 * @since 0.2.0
 */
public class CasperDeployService {

    /**
     * Method to generate a Transfer deploy
     *
     * @param fromPrivateKey sender private Key
     * @param toPublicKey    receiver public key
     * @param amount         amount to transfer
     * @param chainName      network name
     * @return Deploy
     * @throws NoSuchTypeException      thrown if type not found
     * @throws DynamicInstanceException thrown if it could not instantiate a type
     * @throws CLValueEncodeException   thrown if failed to encode a cl value
     * @throws IOException              thrown if an IO error occurs
     * @throws GeneralSecurityException thrown when an error occurs with cryptographic keys
     */
    public static Deploy buildTransferDeploy(AbstractPrivateKey fromPrivateKey,
                                             PublicKey toPublicKey, BigInteger amount, String chainName)
            throws IOException, CLValueEncodeException, DynamicInstanceException, NoSuchTypeException,
            GeneralSecurityException {
        long id = Math.abs(new Random().nextInt());
        BigInteger paymentAmount = BigInteger.valueOf(25000000000L);
        long gasPrice = 1L;
        Ttl ttl = Ttl
                .builder()
                .ttl("30m")
                .build();
        return CasperDeployService.buildTransferDeploy(fromPrivateKey,
                toPublicKey, amount, chainName, id, paymentAmount,
                gasPrice, ttl, new Date(), new ArrayList<>());
    }


    /**
     * @param fromPrivateKey private key of the sender
     * @param toPublicKey    public key of the receiver
     * @param amount         amount to transfer
     * @param id             id field in the request to tag the transaction
     * @param paymentAmount, the number of motes paying to the execution engine
     * @param gasPrice       gasPrice for native transfers can be set to 1
     * @param ttl            time to live in milliseconds (default value is 1800000
     *                       ms (30 minutes))
     * @return Deploy
     * @throws NoSuchTypeException      thrown if type not found
     * @throws DynamicInstanceException thrown if it could not instantiate a type
     * @throws CLValueEncodeException   thrown if failed to encode a cl value
     * @throws IOException              thrown if an IO error occurs
     * @throws GeneralSecurityException thrown when an error occurs with cryptographic keys
     */
    public static Deploy buildTransferDeploy(AbstractPrivateKey fromPrivateKey, PublicKey toPublicKey,
                                             BigInteger amount, String chainName, Long id, BigInteger paymentAmount,
                                             Long gasPrice, Ttl ttl, Date date, List<Digest> dependencies)
            throws IOException, CLValueEncodeException, DynamicInstanceException, NoSuchTypeException,
            GeneralSecurityException {

        List<NamedArg<?>> transferArgs = new LinkedList<>();
        NamedArg<CLTypeU512> amountNamedArg = new NamedArg<>("amount",
                new CLValueU512(amount));
        transferArgs.add(amountNamedArg);
        NamedArg<CLTypePublicKey> publicKeyNamedArg = new NamedArg<>("target",
                new CLValuePublicKey(toPublicKey));
        transferArgs.add(publicKeyNamedArg);
        CLValueOption idArg = new CLValueOption(Optional.of(
                new CLValueU64(BigInteger.valueOf(682008))));
        NamedArg<CLTypeOption> idNamedArg = new NamedArg<>("id", idArg);
        transferArgs.add(idNamedArg);

        Transfer session = Transfer
                .builder()
                .args(transferArgs)
                .build();
        List<NamedArg<?>> paymentArgs = new LinkedList<>();
        NamedArg<CLTypeU512> paymentArg = new NamedArg<>("amount",
                new CLValueU512(paymentAmount));
        paymentArgs.add(paymentArg);

        ModuleBytes payment = ModuleBytes
                .builder()
                .args(paymentArgs)
                .bytes("")
                .build();
        CLValueEncoder clve = new CLValueEncoder();
        payment.encode(clve, true);
        session.encode(clve, true);
        byte[] sessionAnPaymentHash = Blake2b.digest(clve.toByteArray(), 32);
        clve.flush();
        clve.reset();

        PublicKey fromPublicKey = PublicKey.fromAbstractPublicKey(fromPrivateKey.derivePublicKey());

        DeployHeader deployHeader = DeployHeader
                .builder()
                .account(fromPublicKey)
                .ttl(ttl)
                .timeStamp(date)
                .gasPrice(gasPrice)
                .bodyHash(Digest.digestFromBytes(sessionAnPaymentHash))
                .chainName(chainName)
                .dependencies(dependencies)
                .build();
        deployHeader.encode(clve, true);
        byte[] headerHash = Blake2b.digest(clve.toByteArray(), 32);

        Signature signature = Signature.sign(fromPrivateKey, headerHash);

        List<Approval> approvals = new LinkedList<>();
        approvals.add(Approval.builder()
                .signer(PublicKey.fromAbstractPublicKey(fromPrivateKey.derivePublicKey()))
                .signature(signature)
                .build());

        return Deploy.builder()
                .hash(Digest.digestFromBytes(headerHash))
                .header(deployHeader)
                .payment(payment)
                .session(session)
                .approvals(approvals)
                .build();

    }
}