Skip to content

Commit

Permalink
Merge pull request #122 from onflow/block-seal-protobuf
Browse files Browse the repository at this point in the history
FlowBlockSeal protobuf updates
  • Loading branch information
lealobanov authored Oct 25, 2024
2 parents db9e3d1 + 5e02ad8 commit 0166c83
Show file tree
Hide file tree
Showing 15 changed files with 855 additions and 12 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package org.onflow.examples.java.getAccountBalance;

import org.onflow.flow.sdk.FlowAccessApi;
import org.onflow.flow.sdk.FlowAddress;

public class GetAccountBalanceAccessAPIConnector {
private final FlowAccessApi accessAPI;

public GetAccountBalanceAccessAPIConnector(FlowAccessApi accessAPI) {
this.accessAPI = accessAPI;
}

public long getBalanceAtLatestBlock(FlowAddress address) {
FlowAccessApi.AccessApiCallResponse<Long> response = accessAPI.getAccountBalanceAtLatestBlock(address);

if (response instanceof FlowAccessApi.AccessApiCallResponse.Success) {
return ((FlowAccessApi.AccessApiCallResponse.Success<Long>) response).getData();
} else {
FlowAccessApi.AccessApiCallResponse.Error errorResponse = (FlowAccessApi.AccessApiCallResponse.Error) response;
throw new RuntimeException(errorResponse.getMessage(), errorResponse.getThrowable());
}
}

public long getBalanceAtBlockHeight(FlowAddress address, long height) {
FlowAccessApi.AccessApiCallResponse<Long> response = accessAPI.getAccountBalanceAtBlockHeight(address, height);

if (response instanceof FlowAccessApi.AccessApiCallResponse.Success) {
return ((FlowAccessApi.AccessApiCallResponse.Success<Long>) response).getData();
} else {
FlowAccessApi.AccessApiCallResponse.Error errorResponse = (FlowAccessApi.AccessApiCallResponse.Error) response;
throw new RuntimeException(errorResponse.getMessage(), errorResponse.getThrowable());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package org.onflow.examples.java.getAccountBalance;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
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.FlowAddress;
import org.onflow.flow.sdk.FlowBlock;

@FlowEmulatorProjectTest(flowJsonLocation = "../flow/flow.json")
public class GetAccountBalanceAccessAPIConnectorTest {

@FlowTestClient
private FlowAccessApi accessAPI;

@FlowServiceAccountCredentials
private TestAccount serviceAccount;

private GetAccountBalanceAccessAPIConnector balanceAPIConnector;

@BeforeEach
public void setup() {
balanceAPIConnector = new GetAccountBalanceAccessAPIConnector(accessAPI);
}

@Test
public void testCanFetchBalanceAtLatestBlock() {
FlowAddress address = serviceAccount.getFlowAddress();
long balance = balanceAPIConnector.getBalanceAtLatestBlock(address);

Assertions.assertTrue(balance >= 0, "Balance at the latest block should be non-negative");
}

@Test
public void testCanFetchBalanceAtSpecificBlockHeight() {
FlowAddress address = serviceAccount.getFlowAddress();

FlowAccessApi.AccessApiCallResponse<FlowBlock> latestBlockResponse = accessAPI.getLatestBlock(true);

if (latestBlockResponse instanceof FlowAccessApi.AccessApiCallResponse.Success) {
FlowBlock latestBlock = ((FlowAccessApi.AccessApiCallResponse.Success<FlowBlock>) latestBlockResponse).getData();
long blockHeight = latestBlock.getHeight();
long balanceAtHeight = balanceAPIConnector.getBalanceAtBlockHeight(address, blockHeight);

Assertions.assertTrue(balanceAtHeight >= 0, "Balance at specific block height should be non-negative");
} else {
FlowAccessApi.AccessApiCallResponse.Error errorResponse = (FlowAccessApi.AccessApiCallResponse.Error) latestBlockResponse;
Assertions.fail("Failed to fetch the latest block: " + errorResponse.getMessage());
}
}

@Test
public void testBalancesAtLatestBlockAndSpecificHeightShouldMatch() {
FlowAddress address = serviceAccount.getFlowAddress();

long balanceAtLatest = balanceAPIConnector.getBalanceAtLatestBlock(address);
FlowAccessApi.AccessApiCallResponse<FlowBlock> latestBlockResponse = accessAPI.getLatestBlock(true);

if (latestBlockResponse instanceof FlowAccessApi.AccessApiCallResponse.Success) {
FlowBlock latestBlock = ((FlowAccessApi.AccessApiCallResponse.Success<FlowBlock>) latestBlockResponse).getData();
long blockHeight = latestBlock.getHeight();

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

// Ensure balances match
Assertions.assertEquals(balanceAtLatest, balanceAtHeight, "Balance at latest block and specific block height should match");
} else {
FlowAccessApi.AccessApiCallResponse.Error errorResponse = (FlowAccessApi.AccessApiCallResponse.Error) latestBlockResponse;
Assertions.fail("Failed to fetch the latest block: " + errorResponse.getMessage());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package org.onflow.examples.kotlin.getAccountBalance

import org.onflow.flow.sdk.*

internal class GetAccountBalanceAccessAPIConnector(
private val accessAPI: FlowAccessApi
) {
fun getBalanceAtLatestBlock(address: FlowAddress): Long =
when (val response = accessAPI.getAccountBalanceAtLatestBlock(address)) {
is FlowAccessApi.AccessApiCallResponse.Success -> response.data
is FlowAccessApi.AccessApiCallResponse.Error -> throw Exception(response.message, response.throwable)
}

fun getBalanceAtBlockHeight(address: FlowAddress, height: Long): Long =
when (val response = accessAPI.getAccountBalanceAtBlockHeight(address, height)) {
is FlowAccessApi.AccessApiCallResponse.Success -> response.data
is FlowAccessApi.AccessApiCallResponse.Error -> throw Exception(response.message, response.throwable)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
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.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

@FlowEmulatorProjectTest(flowJsonLocation = "../flow/flow.json")
internal class GetAccountBalanceAccessAPIConnectorTest {
@FlowServiceAccountCredentials
lateinit var serviceAccount: TestAccount

@FlowTestClient
lateinit var accessAPI: FlowAccessApi

private lateinit var balanceAPIConnector: GetAccountBalanceAccessAPIConnector

@BeforeEach
fun setup() {
balanceAPIConnector = GetAccountBalanceAccessAPIConnector(accessAPI)
}

@Test
fun `Can fetch account balance at the latest block`() {
val address = serviceAccount.flowAddress
val balance = balanceAPIConnector.getBalanceAtLatestBlock(address)

Assertions.assertNotNull(balance, "Balance should not be null")
Assertions.assertTrue(balance >= 0, "Balance should be non-negative")
}

@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

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}")
}
}

@Test
fun `Balances at the latest block and specific block height should match`() {
val address = serviceAccount.flowAddress

// 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

// 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}")
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,75 @@ class TransactionIntegrationTest {
assertThat(blockHeader.height).isEqualTo(latestBlock.height)
}

@Test
fun `Can get account balance at latest block`() {
val address = serviceAccount.flowAddress

val balanceResponse = try {
handleResult(
accessAPI.getAccountBalanceAtLatestBlock(address),
"Failed to get account balance at latest block"
)
} catch (e: Exception) {
fail("Failed to retrieve account balance at latest block: ${e.message}")
}

assertThat(balanceResponse).isNotNull

val account = try {
handleResult(
accessAPI.getAccountAtLatestBlock(address),
"Failed to get account at latest block"
)
} catch (e: Exception) {
fail("Failed to retrieve account at latest block: ${e.message}")
}

val normalizedBalance = balanceResponse / 100_000_000L

assertThat(normalizedBalance).isEqualTo(account.balance.toBigInteger().longValueExact())
}

@Test
fun `Can get account balance at block height`() {
val address = serviceAccount.flowAddress

val latestBlock = try {
handleResult(
accessAPI.getLatestBlock(true),
"Failed to get latest block"
)
} catch (e: Exception) {
fail("Failed to retrieve latest block: ${e.message}")
}

val height = latestBlock.height

val balanceResponse = try {
handleResult(
accessAPI.getAccountBalanceAtBlockHeight(address, height),
"Failed to get account balance at block height"
)
} catch (e: Exception) {
fail("Failed to retrieve account balance at block height: ${e.message}")
}

assertThat(balanceResponse).isNotNull

val account = try {
handleResult(
accessAPI.getAccountByBlockHeight(address, height),
"Failed to get account by block height"
)
} catch (e: Exception) {
fail("Failed to retrieve account by block height: ${e.message}")
}

val normalizedBalance = balanceResponse / 100_000_000L

assertThat(normalizedBalance).isEqualTo(account.balance.toBigInteger().longValueExact())
}

@Test
fun `Can get latest block`() {
val latestBlock = try {
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 @@ -14,6 +14,10 @@ interface AsyncFlowAccessApi {

fun getLatestBlock(sealed: Boolean = true): CompletableFuture<FlowAccessApi.AccessApiCallResponse<FlowBlock>>

fun getAccountBalanceAtLatestBlock(address: FlowAddress): CompletableFuture<FlowAccessApi.AccessApiCallResponse<Long>>

fun getAccountBalanceAtBlockHeight(address: FlowAddress, height: Long): CompletableFuture<FlowAccessApi.AccessApiCallResponse<Long>>

fun getBlockById(id: FlowId): CompletableFuture<FlowAccessApi.AccessApiCallResponse<FlowBlock?>>

fun getBlockByHeight(height: Long): CompletableFuture<FlowAccessApi.AccessApiCallResponse<FlowBlock?>>
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 @@ -26,6 +26,10 @@ interface FlowAccessApi {

fun getLatestBlock(sealed: Boolean = true): AccessApiCallResponse<FlowBlock>

fun getAccountBalanceAtLatestBlock(address: FlowAddress): AccessApiCallResponse<Long>

fun getAccountBalanceAtBlockHeight(address: FlowAddress, height: Long): AccessApiCallResponse<Long>

fun getBlockById(id: FlowId): AccessApiCallResponse<FlowBlock>

fun getBlockByHeight(height: Long): AccessApiCallResponse<FlowBlock>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,57 @@ class AsyncFlowAccessApiImpl(
}
}

override fun getAccountBalanceAtLatestBlock(address: FlowAddress): CompletableFuture<FlowAccessApi.AccessApiCallResponse<Long>> {
return try {
completableFuture(
try {
api.getAccountBalanceAtLatestBlock(
Access.GetAccountBalanceAtLatestBlockRequest
.newBuilder()
.setAddress(address.byteStringValue)
.build()
)
} catch (e: Exception) {
return CompletableFuture.completedFuture(FlowAccessApi.AccessApiCallResponse.Error("Failed to get account balance at latest block", e))
}
).handle { response, ex ->
if (ex != null) {
FlowAccessApi.AccessApiCallResponse.Error("Failed to get account balance at latest block", ex)
} else {
FlowAccessApi.AccessApiCallResponse.Success(response.balance)
}
}
} catch (e: Exception) {
CompletableFuture.completedFuture(FlowAccessApi.AccessApiCallResponse.Error("Failed to get account balance at latest block", e))
}
}

override fun getAccountBalanceAtBlockHeight(address: FlowAddress, height: Long): CompletableFuture<FlowAccessApi.AccessApiCallResponse<Long>> {
return try {
completableFuture(
try {
api.getAccountBalanceAtBlockHeight(
Access.GetAccountBalanceAtBlockHeightRequest
.newBuilder()
.setAddress(address.byteStringValue)
.setBlockHeight(height)
.build()
)
} catch (e: Exception) {
return CompletableFuture.completedFuture(FlowAccessApi.AccessApiCallResponse.Error("Failed to get account balance at block height", e))
}
).handle { response, ex ->
if (ex != null) {
FlowAccessApi.AccessApiCallResponse.Error("Failed to get account balance at block height", ex)
} else {
FlowAccessApi.AccessApiCallResponse.Success(response.balance)
}
}
} catch (e: Exception) {
CompletableFuture.completedFuture(FlowAccessApi.AccessApiCallResponse.Error("Failed to get account balance at block height", e))
}
}

override fun getBlockById(id: FlowId): CompletableFuture<FlowAccessApi.AccessApiCallResponse<FlowBlock?>> {
return try {
completableFuture(
Expand Down
27 changes: 27 additions & 0 deletions sdk/src/main/kotlin/org/onflow/flow/sdk/impl/FlowAccessApiImpl.kt
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,33 @@ class FlowAccessApiImpl(
FlowAccessApi.AccessApiCallResponse.Error("Failed to get latest block", e)
}

override fun getAccountBalanceAtLatestBlock(address: FlowAddress): FlowAccessApi.AccessApiCallResponse<Long> =
try {
val ret = api.getAccountBalanceAtLatestBlock(
Access.GetAccountBalanceAtLatestBlockRequest
.newBuilder()
.setAddress(address.byteStringValue)
.build()
)
FlowAccessApi.AccessApiCallResponse.Success(ret.balance)
} catch (e: Exception) {
FlowAccessApi.AccessApiCallResponse.Error("Failed to get account balance at latest block", e)
}

override fun getAccountBalanceAtBlockHeight(address: FlowAddress, height: Long): FlowAccessApi.AccessApiCallResponse<Long> =
try {
val ret = api.getAccountBalanceAtBlockHeight(
Access.GetAccountBalanceAtBlockHeightRequest
.newBuilder()
.setAddress(address.byteStringValue)
.setBlockHeight(height)
.build()
)
FlowAccessApi.AccessApiCallResponse.Success(ret.balance)
} catch (e: Exception) {
FlowAccessApi.AccessApiCallResponse.Error("Failed to get account balance at block height", e)
}

override fun getBlockById(id: FlowId): FlowAccessApi.AccessApiCallResponse<FlowBlock> =
try {
val ret = api.getBlockByID(
Expand Down
Loading

0 comments on commit 0166c83

Please sign in to comment.