-
Notifications
You must be signed in to change notification settings - Fork 15
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(kotlin): setup initial kotlin SDK
- Loading branch information
Showing
15 changed files
with
368 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,3 @@ | ||
# Ignore Gradle project-specific cache directory | ||
.gradle | ||
|
||
# Ignore Gradle build output directory | ||
build | ||
.gradle/ | ||
build/ | ||
src/main/resources/com/hedera/hashgraph/sdk/native |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
plugins { | ||
`java-library` | ||
} | ||
|
||
repositories { | ||
mavenCentral() | ||
} | ||
|
||
java { | ||
sourceCompatibility = JavaVersion.VERSION_17 | ||
targetCompatibility = JavaVersion.VERSION_17 | ||
} | ||
|
||
dependencies { | ||
implementation(rootProject) | ||
implementation("com.fasterxml.jackson.core:jackson-databind:2.13.3") | ||
} | ||
|
||
tasks.addRule("Pattern: run<Example>: Runs an example.") { | ||
val taskPattern = this | ||
|
||
if (taskPattern.startsWith("run")) { | ||
val taskName = taskPattern.removePrefix("run") + "Example" | ||
|
||
task<JavaExec>(taskPattern) { | ||
mainClass.set(taskName) | ||
classpath = sourceSets["main"].runtimeClasspath | ||
standardInput = System.`in` | ||
} | ||
} | ||
} |
16 changes: 16 additions & 0 deletions
16
sdk/kotlin/examples/src/main/java/GetAccountBalanceExample.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import com.hedera.hashgraph.sdk.AccountBalanceQuery; | ||
import com.hedera.hashgraph.sdk.AccountId; | ||
import com.hedera.hashgraph.sdk.Client; | ||
|
||
class GetAccountBalanceExample { | ||
public static void main(String[] args) { | ||
var client = Client.forTestnet(); | ||
|
||
var query = new AccountBalanceQuery(); | ||
query.setAccountId(AccountId.parse("0.0.1001")); | ||
|
||
var response = query.execute(client); | ||
|
||
System.out.printf("balance = %s\n", response.balance); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
rootProject.name = "hedera-sdk-kotlin" | ||
include("examples") |
8 changes: 8 additions & 0 deletions
8
sdk/kotlin/src/main/kotlin/com/hedera/hashgraph/sdk/AccountAddress.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package com.hedera.hashgraph.sdk | ||
|
||
/** | ||
* Either `AccountId` or `AccountAlias`. Some transactions and queries | ||
* accept `AccountAddress` as an input. All transactions and queries | ||
* return only `AccountId` as an output, however. | ||
*/ | ||
sealed class AccountAddress constructor(val shard: Long, val realm: Long) |
16 changes: 16 additions & 0 deletions
16
sdk/kotlin/src/main/kotlin/com/hedera/hashgraph/sdk/AccountAlias.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package com.hedera.hashgraph.sdk | ||
|
||
import com.fasterxml.jackson.annotation.JsonValue | ||
|
||
/** | ||
* The unique identifier for a cryptocurrency account represented with an | ||
* alias instead of an account number. | ||
*/ | ||
class AccountAlias(shard: Long, realm: Long, val alias: PublicKey?) : AccountAddress(shard, realm) { | ||
constructor(alias: PublicKey?) : this(0, 0, alias) | ||
|
||
@JsonValue | ||
override fun toString(): String { | ||
return String.format("%d.%d.%s", shard, realm, alias) | ||
} | ||
} |
26 changes: 26 additions & 0 deletions
26
sdk/kotlin/src/main/kotlin/com/hedera/hashgraph/sdk/AccountBalanceQuery.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package com.hedera.hashgraph.sdk | ||
|
||
import com.fasterxml.jackson.annotation.JsonProperty | ||
import com.fasterxml.jackson.annotation.JsonTypeName | ||
|
||
/** | ||
* Get the balance of a cryptocurrency account. | ||
* | ||
* This returns only the balance, so it is a smaller reply | ||
* than `AccountInfoQuery`, which returns the balance plus | ||
* additional information. | ||
*/ | ||
@JsonTypeName("accountBalance") | ||
class AccountBalanceQuery : Query<AccountBalanceResponse>(AccountBalanceResponse::class.java) { | ||
/** | ||
* The account ID for which information is requested. | ||
*/ | ||
@JsonProperty | ||
var accountId: AccountAddress? = null | ||
|
||
/** | ||
* The contract ID for which information is requested. | ||
*/ | ||
@JsonProperty | ||
var contractId: AccountAddress? = null | ||
} |
17 changes: 17 additions & 0 deletions
17
sdk/kotlin/src/main/kotlin/com/hedera/hashgraph/sdk/AccountBalanceResponse.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package com.hedera.hashgraph.sdk | ||
|
||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties | ||
import com.fasterxml.jackson.annotation.JsonProperty | ||
|
||
// TODO: Hbar | ||
// TODO: tokens | ||
@JsonIgnoreProperties("\$type") | ||
class AccountBalanceResponse { | ||
@JsonProperty | ||
@JvmField | ||
val accountId: AccountId? = null | ||
|
||
@JsonProperty | ||
@JvmField | ||
val balance: Long = 0 | ||
} |
35 changes: 35 additions & 0 deletions
35
sdk/kotlin/src/main/kotlin/com/hedera/hashgraph/sdk/AccountId.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package com.hedera.hashgraph.sdk | ||
|
||
import com.fasterxml.jackson.annotation.JsonCreator | ||
import com.fasterxml.jackson.annotation.JsonValue | ||
import jnr.ffi.byref.NativeLongByReference | ||
|
||
/** | ||
* The unique identifier for a cryptocurrency account on Hedera. | ||
*/ | ||
class AccountId(shard: Long, realm: Long, val num: Long) : AccountAddress(shard, realm) { | ||
constructor(num: Long) : this(0, 0, num) | ||
|
||
@JsonValue | ||
override fun toString(): String { | ||
return String.format("%d.%d.%d", shard, realm, num) | ||
} | ||
|
||
companion object { | ||
@JvmStatic | ||
@JsonCreator | ||
fun parse(s: String?): AccountId { | ||
val shard = NativeLongByReference() | ||
val realm = NativeLongByReference() | ||
val num = NativeLongByReference() | ||
|
||
val err = CHedera.instance.hedera_entity_id_from_string(s, shard, realm, num) | ||
|
||
if (err != CHedera.Error.OK) { | ||
throw RuntimeException("oh no") | ||
} | ||
|
||
return AccountId(shard.toLong(), realm.toLong(), num.toLong()) | ||
} | ||
} | ||
} |
128 changes: 128 additions & 0 deletions
128
sdk/kotlin/src/main/kotlin/com/hedera/hashgraph/sdk/CHedera.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
package com.hedera.hashgraph.sdk | ||
|
||
import jnr.ffi.LibraryLoader | ||
import jnr.ffi.LibraryOption | ||
import jnr.ffi.Pointer | ||
import jnr.ffi.annotations.Delegate | ||
import jnr.ffi.annotations.In | ||
import jnr.ffi.annotations.Out | ||
import jnr.ffi.byref.NativeLongByReference | ||
import java.io.File | ||
import java.io.IOException | ||
import java.nio.file.Files | ||
import java.nio.file.StandardCopyOption | ||
import java.util.* | ||
|
||
internal open class CHedera { | ||
companion object { | ||
val instance: LibHedera | ||
|
||
init { | ||
var os = System.getProperty("os.name").lowercase(Locale.getDefault()) | ||
val arch = System.getProperty("os.arch").lowercase(Locale.getDefault()) | ||
var libraryName: String | ||
|
||
if (os.contains("win")) { | ||
os = "windows" | ||
libraryName = "hedera.dll" | ||
} else if (os.contains("mac")) { | ||
os = "macos" | ||
libraryName = "libhedera.dylib" | ||
} else { | ||
os = "linux" | ||
libraryName = "libhedera.so" | ||
} | ||
|
||
val resourceName = String.format("com/hedera/hashgraph/sdk/native/%s/%s/%s", os, arch, libraryName) | ||
|
||
try { | ||
ClassLoader.getSystemClassLoader().getResourceAsStream(resourceName).use { stream -> | ||
if (stream == null) { | ||
throw RuntimeException(String.format("unsupported platform, os: %s, arch: %s", os, arch)) | ||
} | ||
|
||
val temporaryFile = File.createTempFile("chedera", libraryName) | ||
temporaryFile.deleteOnExit() | ||
|
||
Files.copy(stream, temporaryFile.toPath(), StandardCopyOption.REPLACE_EXISTING) | ||
|
||
instance = LibraryLoader.create(LibHedera::class.java) | ||
.option(LibraryOption.LoadNow, true) | ||
.option(LibraryOption.IgnoreError, true) | ||
.failImmediately() | ||
.load(temporaryFile.absolutePath) | ||
} | ||
} catch (e: IOException) { | ||
throw RuntimeException(e) | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Represents any possible result from a fallible function in the Hedera SDK. | ||
*/ | ||
enum class Error { | ||
OK, | ||
TIMED_OUT, | ||
GRPC_STATUS, | ||
FROM_PROTOBUF, | ||
TRANSACTION_PRE_CHECK_STATUS, | ||
TRANSACTION_NO_ID_PRE_CHECK_STATUS, | ||
QUERY_PRE_CHECK_STATUS, | ||
QUERY_PAYMENT_PRE_CHECK_STATUS, | ||
QUERY_NO_PAYMENT_PRE_CHECK_STATUS, | ||
BASIC_PARSE, | ||
KEY_PARSE, | ||
NO_PAYER_ACCOUNT_OR_TRANSACTION_ID, | ||
MAX_QUERY_PAYMENT_EXCEEDED, | ||
NODE_ACCOUNT_UNKNOWN, | ||
RESPONSE_STATUS_UNRECOGNIZED, | ||
RECEIPT_STATUS, | ||
SIGNATURE, | ||
REQUEST_PARSE | ||
} | ||
|
||
fun interface Callback { | ||
@Delegate | ||
operator fun invoke(context: Pointer?, error: Error?, response: String?) | ||
} | ||
|
||
@Suppress("FunctionName") | ||
interface LibHedera { | ||
/** | ||
* Construct a Hedera client pre-configured for testnet access. | ||
*/ | ||
fun hedera_client_for_testnet(): Pointer? | ||
|
||
/** | ||
* Release memory associated with the previously-opened Hedera client. | ||
*/ | ||
fun hedera_client_free(@In client: Pointer?) | ||
|
||
/** | ||
* Parse a Hedera `EntityId` from the passed string. | ||
*/ | ||
fun hedera_entity_id_from_string( | ||
@In s: String?, | ||
@Out shard: NativeLongByReference?, | ||
@Out realm: NativeLongByReference?, | ||
@Out num: NativeLongByReference? | ||
): Error? | ||
|
||
/** | ||
* Returns English-language text that describes the last error. | ||
* Undefined if there has been no last error. | ||
*/ | ||
fun hedera_error_message(): String? | ||
|
||
/** | ||
* Execute this request against the provided client of the Hedera network. | ||
*/ | ||
fun hedera_execute( | ||
@In client: Pointer?, | ||
@In request: String?, | ||
@In context: Pointer?, | ||
@In callback: Callback? | ||
): Error? | ||
} | ||
} |
12 changes: 12 additions & 0 deletions
12
sdk/kotlin/src/main/kotlin/com/hedera/hashgraph/sdk/Client.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package com.hedera.hashgraph.sdk | ||
|
||
import jnr.ffi.Pointer | ||
|
||
class Client private constructor(val ptr: Pointer) { | ||
companion object { | ||
@JvmStatic | ||
fun forTestnet(): Client { | ||
return Client(CHedera.instance.hedera_client_for_testnet()!!) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
60 changes: 60 additions & 0 deletions
60
sdk/kotlin/src/main/kotlin/com/hedera/hashgraph/sdk/Query.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
package com.hedera.hashgraph.sdk | ||
|
||
import com.fasterxml.jackson.annotation.JsonInclude | ||
import com.fasterxml.jackson.annotation.JsonTypeInfo | ||
import com.fasterxml.jackson.core.JsonProcessingException | ||
import com.fasterxml.jackson.databind.ObjectMapper | ||
import java.util.concurrent.CompletableFuture | ||
import java.util.concurrent.ExecutionException | ||
|
||
/** | ||
* A query that can be executed on the Hedera network. | ||
*/ | ||
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "\$type") | ||
open class Query<Response> protected constructor(private val responseClass: Class<Response>) { | ||
fun execute(client: Client): Response { | ||
val objectMapper = ObjectMapper() | ||
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL) | ||
|
||
val request: String = try { | ||
objectMapper.writeValueAsString(this) | ||
} catch (e: JsonProcessingException) { | ||
// BUG: should never happen if our serialization configuration is sane | ||
throw RuntimeException(e) | ||
} | ||
|
||
val completableFuture = CompletableFuture<String?>() | ||
|
||
val executeErr = CHedera.instance.hedera_execute(client.ptr, request, null) { _, responseErr, response -> | ||
if (responseErr !== CHedera.Error.OK) { | ||
// TODO: translate error to exception | ||
System.out.printf("ERROR hedera_execute callback invoked with error, %s\n", responseErr) | ||
|
||
// TODO: completableFuture.completeExceptionally(); | ||
return@hedera_execute | ||
} | ||
|
||
completableFuture.complete(response) | ||
} | ||
|
||
if (executeErr !== CHedera.Error.OK) { | ||
// TODO: translate error to exception | ||
System.out.printf("ERROR hedera_execute returned with error, %s\n", executeErr) | ||
throw RuntimeException() | ||
} | ||
|
||
val response: String? = try { | ||
completableFuture.get() | ||
} catch (e: InterruptedException) { | ||
throw RuntimeException(e) | ||
} catch (e: ExecutionException) { | ||
throw RuntimeException(e) | ||
} | ||
|
||
return try { | ||
objectMapper.readValue(response, responseClass) | ||
} catch (e: JsonProcessingException) { | ||
throw RuntimeException(e) | ||
} | ||
} | ||
} |
Oops, something went wrong.