All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.bbva.hancock.sdk.dlt.ethereum.services.EthereumTransactionService Maven / Gradle / Ivy

package com.bbva.hancock.sdk.dlt.ethereum.services;

import com.bbva.hancock.sdk.HancockSocket;
import com.bbva.hancock.sdk.config.HancockConfig;
import com.bbva.hancock.sdk.dlt.ethereum.models.EthereumRawTransaction;
import com.bbva.hancock.sdk.dlt.ethereum.models.EthereumTransaction;
import com.bbva.hancock.sdk.dlt.ethereum.models.transaction.EthereumSendToProviderRequest;
import com.bbva.hancock.sdk.dlt.ethereum.models.transaction.EthereumSendTransactionRequest;
import com.bbva.hancock.sdk.dlt.ethereum.models.transaction.EthereumSignedTransactionRequest;
import com.bbva.hancock.sdk.dlt.ethereum.models.transaction.EthereumTransactionResponse;
import com.bbva.hancock.sdk.exception.HancockException;
import com.bbva.hancock.sdk.models.TransactionConfig;
import com.google.gson.Gson;
import okhttp3.MediaType;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.web3j.crypto.Credentials;
import org.web3j.crypto.Hash;
import org.web3j.crypto.Sign;
import org.web3j.crypto.TransactionEncoder;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.Web3jFactory;
import org.web3j.protocol.core.methods.response.EthSendTransaction;
import org.web3j.protocol.http.HttpService;
import org.web3j.utils.Numeric;

import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.function.Function;

import static com.bbva.hancock.sdk.Common.*;

public class EthereumTransactionService {

    private static final Logger LOGGER = LoggerFactory.getLogger(EthereumTransactionService.class);
    private static final MediaType CONTENT_TYPE_JSON = MediaType.parse("application/json; charset=utf-8");

    private final HancockConfig config;

    public EthereumTransactionService(final HancockConfig config) {
        this.config = config;
    }

    /**
     * Send a transaction to Ethereum using the configuration pass as parameter
     *
     * @param rawtx    A raw transaction which will be sent to Ethereum
     * @param txConfig Options which will be used to config the transaction
     * @return The result of the request
     * @throws Exception
     */
    public EthereumTransactionResponse send(final EthereumTransaction rawtx, final TransactionConfig txConfig) throws Exception {

        if (txConfig.getPrivateKey() != null) {
            final String signedTransaction = signTransaction(new EthereumRawTransaction(rawtx), txConfig.getPrivateKey());
            if (txConfig.getSendLocally()) {
                return sendSignedTransactionLocally(signedTransaction);
            }
            return sendSignedTransaction(signedTransaction, txConfig);
        } else if (txConfig.getProvider() != null) {
            return sendToSignProvider(rawtx, txConfig);
        }

        return sendRawTransaction(rawtx);
    }

    /**
     * Sign a raw message with the given private key
     *
     * @param message    A raw message which will be signed
     * @param privateKey The private key with which the raw message will be signed
     * @return The signed message as hex string
     */
    public String signMessage(final String message, final String privateKey) {

        final byte[] messageBytes = message.getBytes();
        final byte[] messageHash = Hash.sha3(messageBytes);
        final Credentials credentials = Credentials.create(privateKey);
        final Sign.SignatureData signatureData = Sign.signMessage(messageHash, credentials.getEcKeyPair(), false);

        final String strR = Numeric.toHexString(signatureData.getR(), 0, 32, false);
        final String strS = Numeric.toHexString(signatureData.getS(), 0, 32, false);
        final String strV = Numeric.toHexString(new byte[]{signatureData.getV()}, 0, 1, false);

        return "0x" + (strR.concat(strS).concat(strV));

    }

    /**
     * Sign a raw personal message with the given private key using
     * https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md
     *
     * @param message    A raw message which will be signed
     * @param privateKey The private key with which the raw message will be signed
     * @return The signed message as hex string
     */
    public String signPersonalMessage(final String message, final String privateKey) {

        final String prefix = "\u0019Ethereum Signed Message:\n" + message.length();
        final String msg = prefix + message;

        return signMessage(msg, privateKey);

    }

    /**
     * Sign a raw transaction with a given private key
     *
     * @param rawTransaction A raw transaction which will be signed with the privatekey
     * @param privateKey     The private key with which the raw transaction will be signed
     * @return The signed transaction
     */
    public String signTransaction(final EthereumRawTransaction rawTransaction, final String privateKey) {

        final Credentials credentials = Credentials.create(privateKey);
        final byte[] signedMessage = TransactionEncoder.signMessage(rawTransaction.getWeb3Instance(), credentials);
        final String hexValue = Numeric.toHexString(signedMessage);

        return hexValue;
    }

    /**
     * Send a raw transaction to the ethereum network (It is assumed that the "from" address which will sign the transaction is stored in the node)
     *
     * @param rawTx A raw transaction which will be sent to the network
     * @return The result of the transaction
     * @throws HancockException
     */
    public EthereumTransactionResponse sendRawTransaction(final EthereumTransaction rawTx) throws HancockException {
        final String url = config.getWallet().getHost() + ':' + config.getWallet().getPort() + config.getWallet().getBase() + config.getWallet().getResources().get("sendTx");
        final EthereumSendTransactionRequest sendBody = new EthereumSendTransactionRequest(rawTx);
        final Gson gson = new Gson();
        final String json = gson.toJson(sendBody);
        final RequestBody body = RequestBody.create(CONTENT_TYPE_JSON, json);
        final Request request = getRequest(url, body);

        final Response response = makeCall(request);
        final EthereumTransactionResponse txResponse = checkStatus(response, EthereumTransactionResponse.class);
        return txResponse;
    }

    /**
     * Send a raw transaction to one of the sign providers registered in hancock
     *
     * @param rawTx    A raw transaction which will be signed by the sign provider
     * @param txConfig Options which will be used to config the transaction (provider and callBack Options(optional))
     * @return The result of the request
     * @throws HancockException
     */
    public EthereumTransactionResponse sendToSignProvider(final EthereumTransaction rawTx, final TransactionConfig txConfig) throws HancockException {
        final String url = config.getWallet().getHost() + ':' + config.getWallet().getPort() + config.getWallet().getBase() + config.getWallet().getResources().get("signTx");
        final EthereumSendToProviderRequest sendBody = new EthereumSendToProviderRequest(rawTx, txConfig.getProvider());
        String requestId = "";
        if (txConfig.getCallbackOptions() != null) {
            requestId = txConfig.getCallbackOptions().getRequestId();
            sendBody.setBackUrl(txConfig.getCallbackOptions().getBackUrl());
        }
        final Gson gson = new Gson();
        final String json = gson.toJson(sendBody);
        final RequestBody body = RequestBody.create(CONTENT_TYPE_JSON, json);
        final Request request = new Request.Builder()
                .url(url)
                .post(body)
                .addHeader("vnd-hancock-request-id", requestId)
                .build();

        final Response response = makeCall(request);
        final EthereumTransactionResponse txResponse = checkStatus(response, EthereumTransactionResponse.class);
        return txResponse;
    }

    /**
     * Send a signed transaction to the ethereum network
     *
     * @param signedTransaction A signed transaction which will be send to the network
     * @param txConfig          Options which will be used to config the transaction
     * @return The result of the transaction
     * @throws Exception
     */
    public EthereumTransactionResponse sendSignedTransaction(final String signedTransaction, final TransactionConfig txConfig) throws Exception {

        final String url = config.getWallet().getHost() + ':' + config.getWallet().getPort() + config.getWallet().getBase() + config.getWallet().getResources().get("sendSignedTx");
        final EthereumSignedTransactionRequest signedTxBody = new EthereumSignedTransactionRequest(signedTransaction);
        final Gson gson = new Gson();
        final String json = gson.toJson(signedTxBody);
        final RequestBody body = RequestBody.create(CONTENT_TYPE_JSON, json);
        String requestId = "";

        if (txConfig.getCallbackOptions() != null) {
            requestId = txConfig.getCallbackOptions().getRequestId();
        }

        final Request request = new Request.Builder()
                .url(url)
                .post(body)
                .addHeader("vnd-hancock-request-id", requestId)
                .build();

        final Response response = makeCall(request);
        final EthereumTransactionResponse txResponse = checkStatus(response, EthereumTransactionResponse.class);
        return txResponse;

    }

    /**
     * Send a signed transaction directly to a node of Ethereum
     *
     * @param signedTransaction A signed transaction which will be send to the network
     * @return The transaction hash
     * @throws InterruptedException
     * @throws ExecutionException
     */
    public EthereumTransactionResponse sendSignedTransactionLocally(final String signedTransaction) throws InterruptedException, ExecutionException {

        final String port = config.getNode().getPort() >= 0 ? ":" + String.valueOf(config.getNode().getPort()) : "";

        final Web3j web3j = Web3jFactory.build(new HttpService(config.getNode().getHost() + port));


        final EthSendTransaction ethSendTransaction = web3j.ethSendRawTransaction(signedTransaction).sendAsync().get();

        final Boolean success;

        final String transactionHash = ethSendTransaction.getTransactionHash();

        if (transactionHash != null && !transactionHash.isEmpty()) {
            success = true;
        } else {
            throw new ExecutionException(ethSendTransaction.getError().getMessage(), null);
        }

        final EthereumTransactionResponse response = new EthereumTransactionResponse(success, transactionHash);

        return response;

    }

    /**
     * Create a websocket subscription to watch transactions of type "transactions" in the network
     *
     * @param addresses An array of address that will be added to the watch list
     * @return A HancockSocket object which can add new subscriptions and listen incoming message
     * @throws HancockException
     */
    public HancockSocket subscribe(final List addresses) throws HancockException {
        return subscribe(addresses, "", null);
    }

    /**
     * Create a websocket subscription to watch transactions of type "transactions" in the network
     *
     * @param addresses An array of address that will be added to the watch list
     * @param consumer  A consumer plugin previously configured in hancock that will handle each received event
     * @return A HancockSocket object which can add new subscriptions and listen incoming message
     * @throws HancockException
     */
    public HancockSocket subscribe(final List addresses, final String consumer) throws HancockException {
        return subscribe(addresses, consumer, null);
    }

    /**
     * Create a websocket subscription to watch transactions of type "transactions" in the network
     *
     * @param addresses An array of address that will be added to the watch list
     * @param callback  A function to be called when the sockets has the connection ready. Has the socket as a param
     * @return A HancockSocket object which can add new subscriptions and listen incoming message
     * @throws HancockException
     */
    public HancockSocket subscribe(final List addresses, final Function callback) throws HancockException {
        return subscribe(addresses, "", callback);
    }

    /**
     * Create a websocket subscription to watch transactions of type "transactions" in the network
     *
     * @param addresses An array of address that will be added to the watch list
     * @param consumer  A consumer plugin previously configured in hancock that will handle each received event
     * @param callback  A function to be called when the sockets has the connection ready. Has the socket as a param
     * @return A HancockSocket object which can add new subscriptions and listen incoming message
     * @throws HancockException
     */
    public HancockSocket subscribe(final List addresses, final String consumer, final Function callback) throws HancockException {
        final String url = config.getBroker().getHost() + ':'
                + config.getBroker().getPort()
                + config.getBroker().getBase()
                + config.getBroker().getResources().get("events")
                .replaceAll("__ADDRESS__", "")
                .replaceAll("__SENDER__", "")
                .replaceAll("__CONSUMER__", consumer);
        try {
            final HancockSocket socket = new HancockSocket(url);
            socket.on("ready", o -> {
                socket.watchTransaction(addresses);
                if (callback != null) {
                    callback.apply(socket);
                }
                return null;
            });
            return socket;
        } catch (final HancockException e) {
            LOGGER.error(e.getMessage());
            throw e;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy