
tech.deplant.java4ever.binding.EverSdk Maven / Gradle / Ivy
package tech.deplant.java4ever.binding;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import tech.deplant.java4ever.binding.ffi.EverSdkContext;
import tech.deplant.java4ever.binding.ffi.NativeMethods;
import tech.deplant.java4ever.binding.loader.DefaultLoader;
import tech.deplant.java4ever.binding.loader.DefaultLoaderContext;
import tech.deplant.java4ever.binding.loader.LibraryLoader;
import java.util.Arrays;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.*;
import java.util.function.Consumer;
import java.util.stream.Collectors;
/**
* The type Ever sdk.
*/
public class EverSdk {
/**
* The constant LOG_FORMAT.
*/
public final static String LOG_FORMAT = "CTX:%d REQ:%d FUNC:%s %s:%s";
private final static System.Logger logger = System.getLogger(EverSdk.class.getName());
private final static Map contexts = new ConcurrentHashMap<>();
/**
* Timeout for the waiting of async operations.
*/
public static long timeout = 600_000L;
/**
* Context config client . client config.
*
* @param contextId the context id
* @return the client . client config
*/
public static Client.ClientConfig contextConfig(int contextId) {
return contexts.get(contextId).config();
}
/**
* Gets default workchain id.
*
* @param contextId the context id
* @return the default workchain id
*/
public static long getDefaultWorkchainId(int contextId) {
return switch (contextConfig(contextId).abi()) {
case Client.AbiConfig abiConfig -> Objects.requireNonNullElse(abiConfig.workchain(), 0L);
case null -> 0L;
};
}
/**
* Load.
*/
public static void load() {
load(DefaultLoaderContext.SINGLETON(ClassLoader.getSystemClassLoader()));
}
/**
* Load.
*
* @param loader the loader
*/
public static void load(LibraryLoader loader) {
loader.load();
}
/**
* Async method to call EVER-SDK that do not return responses. It will response as soon as call is sent.
*
* @param function params type parameter
* @param contextId config context id
* @param functionName EVER-SDK function name
* @param functionInputs EVER-SDK function inputs
* @return the completable future with generic result type
* @throws EverSdkException the ever sdk exception
*/
public static
CompletableFuture asyncVoid(final int contextId,
final String functionName,
final P functionInputs) throws EverSdkException {
return contexts.get(contextId).callAsync(functionName, functionInputs, Void.class, null, null);
}
/**
* Async method to get future result from EVER-SDK
*
* @param result type parameter
* @param function params type parameter
* @param contextId config context id
* @param functionName EVER-SDK function name
* @param functionInputs EVER-SDK function inputs
* @param outputClass EVER-SDK output class
* @return the completable future with generic result type
* @throws EverSdkException the ever sdk exception
*/
public static CompletableFuture async(final int contextId,
final String functionName,
final P functionInputs,
final Class outputClass) throws EverSdkException {
return contexts.get(contextId).callAsync(functionName, functionInputs, outputClass, null, null);
}
/**
* Async method to get future result from EVER-SDK with additional parameter to receive recurring events from EVER-SDK
*
* @param result type parameter
* @param function params type parameter
* @param contextId config context id
* @param functionName EVER-SDK function name
* @param functionInputs EVER-SDK function inputs
* @param outputClass EVER-SDK output class
* @param eventConsumer Java Consumer (lambda-function) that accepts JsonNode object returned by EVER-SDK
* @return the completable future with generic result type
* @throws EverSdkException the ever sdk exception
*/
public static CompletableFuture asyncCallback(final int contextId,
final String functionName,
final P functionInputs,
final Class outputClass,
Consumer eventConsumer) throws EverSdkException {
return contexts.get(contextId).callAsync(functionName, functionInputs, outputClass, eventConsumer, null);
}
/**
* Async method to get future result from EVER-SDK with additional parameter to receive AppObject callbacks.
*
* @param result type parameter
* @param function params type parameter
* @param contextId config context id
* @param functionName EVER-SDK function name
* @param functionInputs EVER-SDK function inputs
* @param outputClass EVER-SDK output class
* @param appObject Pointer to AppObject implementation
* @return the completable future with generic result type
* @throws EverSdkException the ever sdk exception
*/
public static CompletableFuture asyncAppObject(final int contextId,
final String functionName,
final P functionInputs,
final Class outputClass,
AppObject appObject) throws EverSdkException {
return contexts.get(contextId).callAsync(functionName, functionInputs, outputClass, null, appObject);
}
/**
* Destroy.
*
* @param contextId the context id
*/
public static void destroy(int contextId) {
NativeMethods.tcDestroyContext(contextId);
}
/**
* Create default int.
*
* @return the int
* @throws EverSdkException the ever sdk exception
*/
public static int createDefault() throws EverSdkException {
return createWithJson("{}");
}
/**
* Creates a builder object that is used to precisely configure EVER-SDK before creating new context.
* After specifying all needed configs in builder style, call build() to finish and create context_id
* with EVER-SDK.
*
* @return the builder
*/
public static Builder builder() {
return new Builder();
}
/**
* Helper method to create new context with only one setting - endpoint of the blockchain.
*
* @param endpoint the endpoint of the blockchain.
* @return context_id for future usage
* @throws EverSdkException the ever sdk exception
*/
public static int createWithEndpoint(String endpoint) throws EverSdkException {
return createWithJson("{ \"network\":{ \"endpoints\": [\"%s\"] } }".formatted(endpoint));
}
/**
* Helper method to create new context from existing config object
*
* @param config config object
* @return context_id for future usage
* @throws EverSdkException the ever sdk exception
*/
public static int createWithConfig(Client.ClientConfig config) throws EverSdkException {
var mergedConfig = new Client.ClientConfig(new Client.BindingConfig(DefaultLoader.BINDING_LIBRARY_NAME,
DefaultLoader.BINDING_LIBRARY_VERSION),
config.network(),
config.crypto(),
config.abi(),
config.boc(),
config.proofs(),
config.localStoragePath());
String resultString = "";
ResultOfCreateContext createContextResponse;
try {
String mergedJson = JsonContext.SDK_JSON_MAPPER().writeValueAsString(mergedConfig);
try {
resultString = NativeMethods.tcCreateContext(mergedJson);
createContextResponse = JsonContext.SDK_JSON_MAPPER()
.readValue(resultString, ResultOfCreateContext.class);
Optional contextId = Optional.ofNullable(createContextResponse.result());
if (contextId.isEmpty() || contextId.get() < 1) {
logger.log(System.Logger.Level.ERROR, () -> "FUNC:sdk.tc_create_context result is empty!");
throw new EverSdkException(new EverSdkException.ErrorResult(-502,
"FUNC:sdk.tc_create_context result is empty!"));
}
int ctxId = contextId.get();
contexts.put(ctxId, new EverSdkContext(ctxId, mergedConfig));
logger.log(System.Logger.Level.TRACE,
() -> "FUNC:sdk.tc_create_context CTX:%d JSON:%s".formatted(ctxId, mergedJson));
return ctxId;
} catch (JsonProcessingException e) {
final String finalResultString = resultString;
logger.log(System.Logger.Level.ERROR,
() -> "FUNC:sdk.tc_create_context request deserialization failed! Exception: %s Result: %s".formatted(
e,
finalResultString));
throw new EverSdkException(new EverSdkException.ErrorResult(-501,
"FUNC:sdk.tc_create_context request deserialization failed! " + e.getMessage() + finalResultString),
e.getCause());
}
} catch (JsonProcessingException e) {
logger.log(System.Logger.Level.ERROR,
() -> "EVER-SDK tc_create_context request serialization failed! Exception: %s Config: %s".formatted(
e,
mergedConfig));
throw new EverSdkException(new EverSdkException.ErrorResult(-502,
"EVER-SDK tc_create_context request serialization failed!"),
e.getCause());
}
}
/**
* Helper method to create new context from existing JSON config
*
* @param configJson json text that contains config parameters
* @return context_id for future usage
* @throws EverSdkException the ever sdk exception
*/
public static int createWithJson(String configJson) throws EverSdkException {
try {
return createWithConfig(JsonContext.SDK_JSON_MAPPER().readValue(configJson, Client.ClientConfig.class));
} catch (JsonProcessingException e) {
logger.log(System.Logger.Level.ERROR,
() -> "EVER-SDK tc_create_context request serialization failed! Exception: %s Config: %s".formatted(
e,
configJson));
throw new EverSdkException(new EverSdkException.ErrorResult(-502,
"EVER-SDK tc_create_context request serialization failed!"),
e.getCause());
}
}
public static Processing.ResultOfProcessMessage sendExternalMessage(int contextId,
String dstAddress,
Abi.ABI abi,
String stateInit,
String messageBody,
String optionalSrcAddress) throws EverSdkException {
var message = EverSdk.await(Boc.encodeExternalInMessage(contextId,
optionalSrcAddress,
dstAddress,
stateInit,
messageBody,
null)).message();
var request = EverSdk.await(Processing.sendMessage(contextId, message, abi, false, null));
return EverSdk.await(Processing.waitForTransaction(contextId,
abi,
message,
request.shardBlockId(),
false,
request.sendingEndpoints(),
null));
}
/**
* Helper method that awaits for completable future for timeout that
* you can specify by issuing EverSdk.timeout = 60_000L.
* All Java Futures possible errors are wrapped in EverSdkException. If you want to catch these errors,
* catch errors -400, -408, -500
*
* @param result type parameter
* @param functionOutputs future result to wait for
* @return returns result of the given type
* @throws EverSdkException the ever sdk exception
*/
public static T await(CompletableFuture functionOutputs) throws EverSdkException {
try {
return functionOutputs.get(timeout, TimeUnit.MILLISECONDS);
} catch (InterruptedException ex3) {
logger.log(System.Logger.Level.ERROR, () -> "EVER-SDK Call interrupted! %s".formatted(ex3.toString()));
throw new EverSdkException(new EverSdkException.ErrorResult(-400, "EVER-SDK call interrupted!"),
ex3.getCause());
} catch (TimeoutException ex4) {
logger.log(System.Logger.Level.ERROR,
() -> "EVER-SDK Call expired on Timeout! %s".formatted(ex4.toString()));
throw new EverSdkException(new EverSdkException.ErrorResult(-408, "EVER-SDK call expired on Timeout!"),
ex4.getCause());
} catch (ExecutionException e) {
if (e.getCause() instanceof EverSdkException everEx) {
throw new EverSdkException(everEx.errorResponse());
} else {
logger.log(System.Logger.Level.ERROR,
() -> "EVER-SDK Call unknown execution exception! %s".formatted(e.toString()));
throw new EverSdkException(new EverSdkException.ErrorResult(-500, "EVER-SDK call expired on Timeout!"),
e.getCause());
}
}
}
/**
* The type Result of create context.
*/
public record ResultOfCreateContext(Integer result, String error) {
}
/**
* The type Builder.
*/
public static class Builder {
private Boolean cacheInLocalStorage = null; // true
private String localStoragePath = null; // "~/.tonclient"
//Context.NetworkConfig
private String[] endpoints = null; // "https://localhost";
@Deprecated private String serverAddress; // deprecated, use endpoints
private Integer networkRetriesCount = null; // 5;
private Integer messageRetriesCount = null; // 5;
private Long messageProcessingTimeout = null; // 40000L;
private Long waitForTimeout = null; // 40000L;
private Long outOfSyncThreshold = null; // 15000L;
private Long reconnectTimeout = null; // 12000L;
private String accessKey = null; //
//Context.CryptoConfig
private Crypto.MnemonicDictionary mnemonicDictionary = null; // Crypto.MnemonicDictionary.English;
private Integer mnemonicWordCount = null; // 12;
private String hdkeyDerivationPath = null; // "m/44'/396'/0'/0/0";
//Context.AbiConfig;
private Long workchain = null; // 0L;
private Long messageExpirationTimeout = null; // 40000L;
private Long messageExpirationTimeoutGrowFactor = null; // null;
private Long cacheMaxSize = null; // 10240L;
private Long maxReconnectTimeout = null; // 120000L;
private Integer sendingEndpointCount = null; // 1;
private Long latencyDetectionInterval = null; // 60000L;
private Long maxLatency = null; // 60000L;
private Long queryTimeout = null; // 60000L;
private Client.NetworkQueriesProtocol queriesProtocol = null; // Client.NetworkQueriesProtocol.HTTP;
private Long firstRempStatusTimeout = null; // 1000L;
private Long nextRempStatusTimeout = null; // 5000L;
private Long signatureId = null;
/**
* Instantiates a new Builder.
*/
public Builder() {
}
/**
* Local storage path builder.
*
* @param localStoragePath the local storage path
* @return the builder
*/
public Builder localStoragePath(String localStoragePath) {
this.localStoragePath = localStoragePath;
return this;
}
/**
* Proofs cache in local storage builder.
*
* @param cacheInLocalStorage the cache in local storage
* @return the builder
*/
public Builder proofsCacheInLocalStorage(boolean cacheInLocalStorage) {
this.cacheInLocalStorage = cacheInLocalStorage;
return this;
}
/**
* Network max reconnect timeout builder.
*
* @param maxReconnectTimeout the max reconnect timeout
* @return the builder
*/
public Builder networkMaxReconnectTimeout(Long maxReconnectTimeout) {
this.maxReconnectTimeout = maxReconnectTimeout;
return this;
}
/**
* Network sending endpoint count builder.
*
* @param sendingEndpointCount the sending endpoint count
* @return the builder
*/
public Builder networkSendingEndpointCount(Integer sendingEndpointCount) {
this.sendingEndpointCount = sendingEndpointCount;
return this;
}
/**
* Network latency detection interval builder.
*
* @param latencyDetectionInterval the latency detection interval
* @return the builder
*/
public Builder networkLatencyDetectionInterval(Long latencyDetectionInterval) {
this.latencyDetectionInterval = latencyDetectionInterval;
return this;
}
/**
* Network max latency builder.
*
* @param maxLatency the max latency
* @return the builder
*/
public Builder networkMaxLatency(Long maxLatency) {
this.maxLatency = maxLatency;
return this;
}
/**
* Network query timeout builder.
*
* @param queryTimeout the query timeout
* @return the builder
*/
public Builder networkQueryTimeout(Long queryTimeout) {
this.queryTimeout = queryTimeout;
return this;
}
/**
* Network queries protocol builder.
*
* @param queriesProtocol the queries protocol
* @return the builder
*/
public Builder networkQueriesProtocol(Client.NetworkQueriesProtocol queriesProtocol) {
this.queriesProtocol = queriesProtocol;
return this;
}
/**
* Network first remp status timeout builder.
*
* @param firstRempStatusTimeout the first remp status timeout
* @return the builder
*/
public Builder networkFirstRempStatusTimeout(Long firstRempStatusTimeout) {
this.firstRempStatusTimeout = firstRempStatusTimeout;
return this;
}
/**
* Network next remp status timeout builder.
*
* @param nextRempStatusTimeout the next remp status timeout
* @return the builder
*/
public Builder networkNextRempStatusTimeout(Long nextRempStatusTimeout) {
this.nextRempStatusTimeout = nextRempStatusTimeout;
return this;
}
/**
* Network endpoints builder.
*
* @param endpoints the endpoints
* @return the builder
*/
public Builder networkEndpoints(String... endpoints) {
this.endpoints = endpoints;
return this;
}
/**
* Network server address builder.
*
* @param server_address the server address
* @return the builder
*/
public Builder networkServerAddress(String server_address) {
this.serverAddress = server_address;
return this;
}
/**
* Network retries count builder.
*
* @param network_retries_count the network retries count
* @return the builder
*/
public Builder networkRetriesCount(Integer network_retries_count) {
this.networkRetriesCount = network_retries_count;
return this;
}
/**
* Network message retries count builder.
*
* @param message_retries_count the message retries count
* @return the builder
*/
public Builder networkMessageRetriesCount(Integer message_retries_count) {
this.messageRetriesCount = message_retries_count;
return this;
}
/**
* Network message processing timeout builder.
*
* @param message_processing_timeout the message processing timeout
* @return the builder
*/
public Builder networkMessageProcessingTimeout(Long message_processing_timeout) {
this.messageProcessingTimeout = message_processing_timeout;
return this;
}
/**
* Network wait for timeout builder.
*
* @param wait_for_timeout the wait for timeout
* @return the builder
*/
public Builder networkWaitForTimeout(Long wait_for_timeout) {
this.waitForTimeout = wait_for_timeout;
return this;
}
/**
* Network out of sync threshold builder.
*
* @param out_of_sync_threshold the out of sync threshold
* @return the builder
*/
public Builder networkOutOfSyncThreshold(Long out_of_sync_threshold) {
this.outOfSyncThreshold = out_of_sync_threshold;
return this;
}
/**
* Network reconnect timeout builder.
*
* @param reconnect_timeout the reconnect timeout
* @return the builder
*/
public Builder networkReconnectTimeout(Long reconnect_timeout) {
this.reconnectTimeout = reconnect_timeout;
return this;
}
/**
* Network signature id builder.
*
* @param signatureId the signature id
* @return the builder
*/
public Builder networkSignatureId(Long signatureId) {
this.signatureId = signatureId;
return this;
}
/**
* Network access key builder.
*
* @param access_key the access key
* @return the builder
*/
public Builder networkAccessKey(String access_key) {
this.accessKey = access_key;
return this;
}
/**
* Crypto mnemonic dictionary builder.
*
* @param mnemonic_dictionary the mnemonic dictionary
* @return the builder
*/
//cripto
public Builder cryptoMnemonicDictionary(Crypto.MnemonicDictionary mnemonic_dictionary) {
this.mnemonicDictionary = mnemonic_dictionary;
return this;
}
/**
* Crypto mnemonic word count builder.
*
* @param mnemonic_word_count the mnemonic word count
* @return the builder
*/
public Builder cryptoMnemonicWordCount(Integer mnemonic_word_count) {
this.mnemonicWordCount = mnemonic_word_count;
return this;
}
/**
* Crypto hdkey derivation path builder.
*
* @param hdkey_derivation_path the hdkey derivation path
* @return the builder
*/
public Builder cryptoHdkeyDerivationPath(String hdkey_derivation_path) {
this.hdkeyDerivationPath = hdkey_derivation_path;
return this;
}
/**
* Abi workchain builder.
*
* @param workchain the workchain
* @return the builder
*/
//abi
public Builder abiWorkchain(Long workchain) {
this.workchain = workchain;
return this;
}
/**
* Abi message expiration timeout builder.
*
* @param message_expiration_timeout the message expiration timeout
* @return the builder
*/
public Builder abiMessageExpirationTimeout(Long message_expiration_timeout) {
this.messageExpirationTimeout = message_expiration_timeout;
return this;
}
/**
* Abi message expiration timeout grow factor builder.
*
* @param message_expiration_timeout_grow_factor the message expiration timeout grow factor
* @return the builder
*/
public Builder abiMessageExpirationTimeoutGrowFactor(Long message_expiration_timeout_grow_factor) {
this.messageExpirationTimeoutGrowFactor = message_expiration_timeout_grow_factor;
return this;
}
/**
* Boc cache max size builder.
*
* @param cacheMaxSize the cache max size
* @return the builder
*/
public Builder bocCacheMaxSize(Long cacheMaxSize) {
this.cacheMaxSize = cacheMaxSize;
return this;
}
private Client.NetworkConfig buildNetworkConfig() {
if (this.serverAddress == null && this.endpoints == null && this.networkRetriesCount == null &&
this.maxReconnectTimeout == null && this.reconnectTimeout == null && this.messageRetriesCount == null &&
this.messageProcessingTimeout == null && this.waitForTimeout == null &&
this.outOfSyncThreshold == null && this.sendingEndpointCount == null &&
this.latencyDetectionInterval == null && this.maxLatency == null && this.queryTimeout == null &&
this.queriesProtocol == null && this.firstRempStatusTimeout == null &&
this.nextRempStatusTimeout == null && this.signatureId == null && this.accessKey == null) {
return null;
} else {
return new Client.NetworkConfig(this.serverAddress,
this.endpoints,
this.networkRetriesCount,
this.maxReconnectTimeout,
this.reconnectTimeout,
this.messageRetriesCount,
this.messageProcessingTimeout,
this.waitForTimeout,
this.outOfSyncThreshold,
this.sendingEndpointCount,
this.latencyDetectionInterval,
this.maxLatency,
this.queryTimeout,
this.queriesProtocol,
this.firstRempStatusTimeout,
this.nextRempStatusTimeout,
this.signatureId,
this.accessKey);
}
}
private Client.CryptoConfig buildCryptoConfig() {
if (this.mnemonicDictionary == null && this.mnemonicWordCount == null && this.hdkeyDerivationPath == null) {
return null;
} else {
return new Client.CryptoConfig(this.mnemonicDictionary,
this.mnemonicWordCount,
this.hdkeyDerivationPath);
}
}
private Client.AbiConfig buildAbiConfig() {
if (this.workchain == null && this.messageExpirationTimeout == null &&
this.messageExpirationTimeoutGrowFactor == null) {
return null;
} else {
return new Client.AbiConfig(this.workchain,
this.messageExpirationTimeout,
this.messageExpirationTimeoutGrowFactor);
}
}
private Client.BocConfig buildBocConfig() {
if (this.cacheMaxSize == null) {
return null;
} else {
return new Client.BocConfig(this.cacheMaxSize);
}
}
private Client.ProofsConfig buildProofsConfig() {
if (this.cacheInLocalStorage == null) {
return null;
} else {
return new Client.ProofsConfig(this.cacheInLocalStorage);
}
}
/**
* Build int.
*
* @return the int
* @throws EverSdkException the ever sdk exception
*/
public int build() throws EverSdkException {
var config = new Client.ClientConfig(new Client.BindingConfig("java4ever", "3.0.0"),
buildNetworkConfig(),
buildCryptoConfig(),
buildAbiConfig(),
buildBocConfig(),
buildProofsConfig(),
this.localStoragePath);
return createWithConfig(config);
}
}
}