Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add GetSystemTransaction and GetSystemTransactionResult endpoints #143

Merged
merged 15 commits into from
Nov 22, 2024
Merged
2 changes: 2 additions & 0 deletions java-example/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,9 @@ Below is a list of all Java code examples currently supported in this repo:
[Get transactions.](src/main/java/org/onflow/examples/java/getTransaction/GetTransactionAccessAPIConnector.java)

- Get transaction
- Get system transaction
- Get transaction result
- Get system transaction result
- Get transaction result by index

#### Sending Transactions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,16 @@ public FlowTransaction getTransaction(FlowId txID) {
}
}

public FlowTransaction getSystemTransaction(FlowId blockId) {
FlowAccessApi.AccessApiCallResponse<FlowTransaction> response = accessAPI.getSystemTransaction(blockId);
if (response instanceof FlowAccessApi.AccessApiCallResponse.Success) {
return ((FlowAccessApi.AccessApiCallResponse.Success<FlowTransaction>) response).getData();
} else {
FlowAccessApi.AccessApiCallResponse.Error errorResponse = (FlowAccessApi.AccessApiCallResponse.Error) response;
throw new RuntimeException(errorResponse.getMessage(), errorResponse.getThrowable());
}
}

public FlowTransactionResult getTransactionResult(FlowId txID) {
FlowAccessApi.AccessApiCallResponse<FlowTransactionResult> response = accessAPI.getTransactionResultById(txID);
if (response instanceof FlowAccessApi.AccessApiCallResponse.Success) {
Expand All @@ -32,6 +42,16 @@ public FlowTransactionResult getTransactionResult(FlowId txID) {
}
}

public FlowTransactionResult getSystemTransactionResult(FlowId blockId) {
FlowAccessApi.AccessApiCallResponse<FlowTransactionResult> response = accessAPI.getSystemTransactionResult(blockId);
if (response instanceof FlowAccessApi.AccessApiCallResponse.Success) {
return ((FlowAccessApi.AccessApiCallResponse.Success<FlowTransactionResult>) response).getData();
} else {
FlowAccessApi.AccessApiCallResponse.Error errorResponse = (FlowAccessApi.AccessApiCallResponse.Error) response;
throw new RuntimeException(errorResponse.getMessage(), errorResponse.getThrowable());
}
}

public FlowTransactionResult getTransactionResultByIndex(FlowId blockId, Integer index) {
FlowAccessApi.AccessApiCallResponse<FlowTransactionResult> response = accessAPI.getTransactionResultByIndex(blockId, index);
if (response instanceof FlowAccessApi.AccessApiCallResponse.Success) {
Expand Down
2 changes: 2 additions & 0 deletions kotlin-example/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,9 @@ Below is a list of all Kotlin code examples currently supported in this repo:
[Get transactions.](src/main/kotlin/org/onflow/examples/kotlin/getTransaction/GetTransactionAccessAPIConnector.kt)

- Get transaction
- Get system transaction
- Get transaction result
- Get system transaction result
- Get transaction result by index

#### Sending Transactions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,24 @@ class GetTransactionAccessAPIConnector(
is FlowAccessApi.AccessApiCallResponse.Error -> throw Exception(response.message, response.throwable)
}

fun getSystemTransaction(blockId: FlowId): FlowTransaction =
when (val response = accessAPI.getSystemTransaction(blockId)) {
is FlowAccessApi.AccessApiCallResponse.Success -> response.data
is FlowAccessApi.AccessApiCallResponse.Error -> throw Exception(response.message, response.throwable)
}

fun getTransactionResult(txID: FlowId): FlowTransactionResult =
when (val response = accessAPI.getTransactionResultById(txID)) {
is FlowAccessApi.AccessApiCallResponse.Success -> response.data
is FlowAccessApi.AccessApiCallResponse.Error -> throw Exception(response.message, response.throwable)
}

fun getSystemTransactionResult(blockId: FlowId): FlowTransactionResult =
when (val response = accessAPI.getSystemTransactionResult(blockId)) {
is FlowAccessApi.AccessApiCallResponse.Success -> response.data
is FlowAccessApi.AccessApiCallResponse.Error -> throw Exception(response.message, response.throwable)
}

fun getTransactionResultByIndex(blockId: FlowId, index: Int): FlowTransactionResult =
when (val response = accessAPI.getTransactionResultByIndex(blockId, index)) {
is FlowAccessApi.AccessApiCallResponse.Success -> response.data
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ package org.onflow.examples.kotlin.getAccountBalance
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.onflow.examples.kotlin.getTransaction.GetTransactionAccessAPIConnectorTest
import org.onflow.flow.common.test.FlowEmulatorProjectTest
import org.onflow.flow.common.test.FlowServiceAccountCredentials
import org.onflow.flow.common.test.FlowTestClient
import org.onflow.flow.common.test.TestAccount
import org.onflow.flow.sdk.FlowAccessApi
import org.onflow.flow.sdk.FlowBlock

@FlowEmulatorProjectTest(flowJsonLocation = "../flow/flow.json")
internal class GetAccountBalanceAccessAPIConnectorTest {
Expand All @@ -18,10 +20,12 @@ internal class GetAccountBalanceAccessAPIConnectorTest {
lateinit var accessAPI: FlowAccessApi

private lateinit var balanceAPIConnector: GetAccountBalanceAccessAPIConnector
private lateinit var latestBlock: FlowBlock

@BeforeEach
fun setup() {
balanceAPIConnector = GetAccountBalanceAccessAPIConnector(accessAPI)
latestBlock = GetTransactionAccessAPIConnectorTest.fetchLatestBlockWithRetries(accessAPI)
}

@Test
Expand All @@ -36,17 +40,10 @@ internal class GetAccountBalanceAccessAPIConnectorTest {
@Test
fun `Can fetch account balance at a specific block height`() {
val address = serviceAccount.flowAddress
val latestBlock = accessAPI.getLatestBlock(true) // Fetch the latest sealed block
val balanceAtHeight = balanceAPIConnector.getBalanceAtBlockHeight(address, latestBlock.height)

when (latestBlock) {
is FlowAccessApi.AccessApiCallResponse.Success -> {
val balanceAtHeight = balanceAPIConnector.getBalanceAtBlockHeight(address, latestBlock.data.height)

Assertions.assertNotNull(balanceAtHeight, "Balance at specific block height should not be null")
Assertions.assertTrue(balanceAtHeight >= 0, "Balance at specific block height should be non-negative")
}
is FlowAccessApi.AccessApiCallResponse.Error -> Assertions.fail("Failed to retrieve the latest block: ${latestBlock.message}")
}
Assertions.assertNotNull(balanceAtHeight, "Balance at specific block height should not be null")
Assertions.assertTrue(balanceAtHeight >= 0, "Balance at specific block height should be non-negative")
}

@Test
Expand All @@ -56,18 +53,11 @@ internal class GetAccountBalanceAccessAPIConnectorTest {
// Fetch balance at latest block
val balanceAtLatest = balanceAPIConnector.getBalanceAtLatestBlock(address)

// Fetch latest block height
val latestBlock = accessAPI.getLatestBlock(true)
when (latestBlock) {
is FlowAccessApi.AccessApiCallResponse.Success -> {
val blockHeight = latestBlock.data.height
val blockHeight = latestBlock.height

// Fetch balance at the same block height
val balanceAtHeight = balanceAPIConnector.getBalanceAtBlockHeight(address, blockHeight)
// Fetch balance at the same block height
val balanceAtHeight = balanceAPIConnector.getBalanceAtBlockHeight(address, blockHeight)

Assertions.assertEquals(balanceAtLatest, balanceAtHeight, "Balance at latest block and specific block height should match")
}
is FlowAccessApi.AccessApiCallResponse.Error -> Assertions.fail("Failed to retrieve the latest block: ${latestBlock.message}")
}
Assertions.assertEquals(balanceAtLatest, balanceAtHeight, "Balance at latest block and specific block height should match")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.onflow.examples.kotlin.AccessAPIConnector
import org.onflow.examples.kotlin.getTransaction.GetTransactionAccessAPIConnectorTest
import org.onflow.flow.common.test.FlowEmulatorProjectTest
import org.onflow.flow.common.test.FlowServiceAccountCredentials
import org.onflow.flow.common.test.FlowTestClient
Expand All @@ -23,6 +24,7 @@ class GetCollectionAccessAPIConnectorTest {
private lateinit var accessAPIConnector: AccessAPIConnector

private lateinit var collectionId: FlowId
lateinit var block: FlowBlock

@BeforeEach
fun setup() {
Expand All @@ -36,10 +38,7 @@ class GetCollectionAccessAPIConnectorTest {
publicKey
)

val block = when (val response = accessAPI.getLatestBlock()) {
is FlowAccessApi.AccessApiCallResponse.Success -> response.data
is FlowAccessApi.AccessApiCallResponse.Error -> throw Exception(response.message, response.throwable)
}
block = GetTransactionAccessAPIConnectorTest.fetchLatestBlockWithRetries(accessAPI)
collectionId = block.collectionGuarantees.first().id
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package org.onflow.examples.kotlin.getProtocolState
import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.onflow.examples.kotlin.getTransaction.GetTransactionAccessAPIConnectorTest
import org.onflow.flow.common.test.FlowEmulatorProjectTest
import org.onflow.flow.common.test.FlowTestClient
import org.onflow.flow.sdk.FlowAccessApi
Expand All @@ -20,6 +21,7 @@ internal class GetProtocolStateAccessAPIConnectorTest {
@BeforeEach
fun setup() {
protocolStateConnector = GetProtocolStateAccessAPIConnector(accessAPI)
block = GetTransactionAccessAPIConnectorTest.fetchLatestBlockWithRetries(accessAPI)
}

@Test
Expand All @@ -30,22 +32,12 @@ internal class GetProtocolStateAccessAPIConnectorTest {

@Test
fun `Can get protocol state snapshot by blockId`() {
block = when (val response = accessAPI.getLatestBlock()) {
is FlowAccessApi.AccessApiCallResponse.Success -> response.data
is FlowAccessApi.AccessApiCallResponse.Error -> throw Exception(response.message, response.throwable)
}

val latestSnapshot: FlowSnapshot = protocolStateConnector.getProtocolStateSnapshotByBlockId(block.id)
assertNotNull(latestSnapshot, ("Snapshot should not be null"))
}

@Test
fun `Can get protocol state snapshot by height`() {
block = when (val response = accessAPI.getLatestBlock()) {
is FlowAccessApi.AccessApiCallResponse.Success -> response.data
is FlowAccessApi.AccessApiCallResponse.Error -> throw Exception(response.message, response.throwable)
}

val latestSnapshot: FlowSnapshot = protocolStateConnector.getProtocolStateSnapshotByHeight(block.height)
assertNotNull(latestSnapshot, ("Snapshot should not be null"))
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package org.onflow.examples.kotlin.getTransaction

import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking
import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
Expand Down Expand Up @@ -37,10 +39,7 @@ internal class GetTransactionAccessAPIConnectorTest {
publicKey
)

block = when (val response = accessAPI.getLatestBlock()) {
is FlowAccessApi.AccessApiCallResponse.Success -> response.data
is FlowAccessApi.AccessApiCallResponse.Error -> throw Exception(response.message, response.throwable)
}
block = fetchLatestBlockWithRetries(accessAPI)
}

@Test
Expand All @@ -66,4 +65,19 @@ internal class GetTransactionAccessAPIConnectorTest {
assertNotNull(transactionResult, "Transaction result should not be null")
assertTrue(transactionResult.status === FlowTransactionStatus.SEALED, "Transaction should be sealed")
}

companion object {
fun fetchLatestBlockWithRetries(accessAPI: FlowAccessApi, retries: Int = 5, delayMillis: Long = 500): FlowBlock {
repeat(retries) { attempt ->
when (val response = accessAPI.getLatestBlock()) {
is FlowAccessApi.AccessApiCallResponse.Success -> return response.data
is FlowAccessApi.AccessApiCallResponse.Error -> {
println("Attempt ${attempt + 1} failed: ${response.message}. Retrying...")
runBlocking { delay(delayMillis) }
}
}
}
throw Exception("Failed to retrieve the latest block after $retries attempts.")
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package org.onflow.flow.sdk.transaction

import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking
import org.onflow.flow.sdk.*
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
Expand Down Expand Up @@ -43,8 +45,22 @@ class TransactionIntegrationTest {
fail("$errorMessage: ${e.message}")
}

private fun getLatestBlock(): FlowBlock =
safelyHandle({ Result.success(handleResult(accessAPI.getLatestBlock(true), LATEST_BLOCK_ERROR)) }, LATEST_BLOCK_ERROR)
private fun getLatestBlock(retries: Int = 5, delayMillis: Long = 500): FlowBlock {
repeat(retries) { attempt ->
try {
return safelyHandle(
{ Result.success(handleResult(accessAPI.getLatestBlock(true), LATEST_BLOCK_ERROR)) },
LATEST_BLOCK_ERROR
)
} catch (e: Exception) {
if (attempt == retries - 1) {
throw Exception("$LATEST_BLOCK_ERROR after $retries attempts", e)
}
runBlocking { delay(delayMillis) }
}
}
throw Exception(LATEST_BLOCK_ERROR)
}

private fun getAccountAtLatestBlock(address: FlowAddress): FlowAccount =
safelyHandle({ Result.success(handleResult(accessAPI.getAccountAtLatestBlock(address), ACCOUNT_ERROR)) }, ACCOUNT_ERROR)
Expand Down
4 changes: 4 additions & 0 deletions sdk/src/main/kotlin/org/onflow/flow/sdk/AsyncFlowAccessApi.kt
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ interface AsyncFlowAccessApi {

fun getTransactionResultById(id: FlowId): CompletableFuture<FlowAccessApi.AccessApiCallResponse<FlowTransactionResult?>>

fun getSystemTransaction(blockId: FlowId): CompletableFuture<FlowAccessApi.AccessApiCallResponse<FlowTransaction?>>

fun getSystemTransactionResult(blockId: FlowId): CompletableFuture<FlowAccessApi.AccessApiCallResponse<FlowTransactionResult?>>

fun getTransactionResultByIndex(blockId: FlowId, index: Int): CompletableFuture<FlowAccessApi.AccessApiCallResponse<FlowTransactionResult>>

@Deprecated(
Expand Down
4 changes: 4 additions & 0 deletions sdk/src/main/kotlin/org/onflow/flow/sdk/FlowAccessApi.kt
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ interface FlowAccessApi {

fun getTransactionResultById(id: FlowId): AccessApiCallResponse<FlowTransactionResult>

fun getSystemTransaction(blockId: FlowId): AccessApiCallResponse<FlowTransaction>

fun getSystemTransactionResult(blockId: FlowId): AccessApiCallResponse<FlowTransactionResult>

fun getTransactionResultByIndex(blockId: FlowId, index: Int): AccessApiCallResponse<FlowTransactionResult>

@Deprecated(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,34 @@ class AsyncFlowAccessApiImpl(
errorMessage = "Failed to get transaction result by index"
)

override fun getSystemTransaction(blockId: FlowId): CompletableFuture<FlowAccessApi.AccessApiCallResponse<FlowTransaction?>> =
handleApiCall(
apiCall = {
api.getSystemTransaction(
Access.GetSystemTransactionRequest
.newBuilder()
.setBlockId(blockId.byteStringValue)
.build()
)
},
transform = { if (it.hasTransaction()) FlowTransaction.of(it.transaction) else null },
errorMessage = "Failed to get system transaction by block ID"
)

override fun getSystemTransactionResult(blockId: FlowId): CompletableFuture<FlowAccessApi.AccessApiCallResponse<FlowTransactionResult?>> =
handleApiCall(
apiCall = {
api.getSystemTransactionResult(
Access.GetSystemTransactionResultRequest
.newBuilder()
.setBlockId(blockId.byteStringValue)
.build()
)
},
transform = { FlowTransactionResult.of(it) },
errorMessage = "Failed to get system transaction result by block ID"
)

private fun executeScript(
apiCall: () -> ListenableFuture<Access.ExecuteScriptResponse>,
errorMessage: String
Expand Down
Loading
Loading