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

Setting up new Access API methods #61

Merged
merged 4 commits into from
Jul 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/main/kotlin/org/onflow/flow/sdk/AsyncFlowAccessApi.kt
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,10 @@ interface AsyncFlowAccessApi {
fun getNetworkParameters(): CompletableFuture<FlowAccessApi.AccessApiCallResponse<FlowChainId>>

fun getLatestProtocolStateSnapshot(): CompletableFuture<FlowAccessApi.AccessApiCallResponse<FlowSnapshot>>

fun getTransactionsByBlockId(id: FlowId): CompletableFuture<FlowAccessApi.AccessApiCallResponse<List<FlowTransaction>>>

fun getTransactionResultsByBlockId(id: FlowId): CompletableFuture<FlowAccessApi.AccessApiCallResponse<List<FlowTransactionResult>>>

fun getExecutionResultByBlockId(id: FlowId): CompletableFuture<FlowAccessApi.AccessApiCallResponse<FlowExecutionResult?>>
}
6 changes: 6 additions & 0 deletions src/main/kotlin/org/onflow/flow/sdk/FlowAccessApi.kt
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,10 @@ interface FlowAccessApi {
fun getNetworkParameters(): AccessApiCallResponse<FlowChainId>

fun getLatestProtocolStateSnapshot(): AccessApiCallResponse<FlowSnapshot>

fun getTransactionsByBlockId(id: FlowId): AccessApiCallResponse<List<FlowTransaction>>

fun getTransactionResultsByBlockId(id: FlowId): AccessApiCallResponse<List<FlowTransactionResult>>

fun getExecutionResultByBlockId(id: FlowId): AccessApiCallResponse<FlowExecutionResult>
}
68 changes: 68 additions & 0 deletions src/main/kotlin/org/onflow/flow/sdk/impl/AsyncFlowAccessApiImpl.kt
Original file line number Diff line number Diff line change
Expand Up @@ -513,6 +513,74 @@ class AsyncFlowAccessApiImpl(
CompletableFuture.completedFuture(FlowAccessApi.AccessApiCallResponse.Error("Failed to get latest protocol state snapshot", e))
}
}

override fun getTransactionsByBlockId(id: FlowId): CompletableFuture<FlowAccessApi.AccessApiCallResponse<List<FlowTransaction>>> {
return try {
completableFuture(
try {
api.getTransactionsByBlockID(
Access.GetTransactionsByBlockIDRequest.newBuilder().setBlockId(id.byteStringValue).build()
)
} catch (e: Exception) {
return CompletableFuture.completedFuture(FlowAccessApi.AccessApiCallResponse.Error("Failed to get transactions by block ID", e))
}
).handle { response, ex ->
if (ex != null) {
FlowAccessApi.AccessApiCallResponse.Error("Failed to get transactions by block ID", ex)
} else {
FlowAccessApi.AccessApiCallResponse.Success(response.transactionsList.map { FlowTransaction.of(it) })
}
}
} catch (e: Exception) {
CompletableFuture.completedFuture(FlowAccessApi.AccessApiCallResponse.Error("Failed to get transactions by block ID", e))
}
}

override fun getTransactionResultsByBlockId(id: FlowId): CompletableFuture<FlowAccessApi.AccessApiCallResponse<List<FlowTransactionResult>>> {
return try {
completableFuture(
try {
api.getTransactionResultsByBlockID(
Access.GetTransactionsByBlockIDRequest.newBuilder().setBlockId(id.byteStringValue).build()
)
} catch (e: Exception) {
return CompletableFuture.completedFuture(FlowAccessApi.AccessApiCallResponse.Error("Failed to get transaction results by block ID", e))
}
).handle { response, ex ->
if (ex != null) {
FlowAccessApi.AccessApiCallResponse.Error("Failed to get transaction results by block ID", ex)
} else {
FlowAccessApi.AccessApiCallResponse.Success(response.transactionResultsList.map { FlowTransactionResult.of(it) })
}
}
} catch (e: Exception) {
CompletableFuture.completedFuture(FlowAccessApi.AccessApiCallResponse.Error("Failed to get transaction results by block ID", e))
}
}

override fun getExecutionResultByBlockId(id: FlowId): CompletableFuture<FlowAccessApi.AccessApiCallResponse<FlowExecutionResult?>> {
return try {
completableFuture(
try {
api.getExecutionResultByID(Access.GetExecutionResultByIDRequest.newBuilder().setId(id.byteStringValue).build())
} catch (e: Exception) {
return CompletableFuture.completedFuture(FlowAccessApi.AccessApiCallResponse.Error("Failed to get execution result by block ID", e))
}
).handle { response, ex ->
if (ex != null) {
FlowAccessApi.AccessApiCallResponse.Error("Failed to get execution result by block ID", ex)
} else {
if (response.hasExecutionResult()) {
FlowAccessApi.AccessApiCallResponse.Success(FlowExecutionResult.of(response))
} else {
FlowAccessApi.AccessApiCallResponse.Error("Execution result not found")
}
}
}
} catch (e: Exception) {
CompletableFuture.completedFuture(FlowAccessApi.AccessApiCallResponse.Error("Failed to get execution result by block ID", e))
}
}
}

fun <T> completableFuture(future: ListenableFuture<T>): CompletableFuture<T> {
Expand Down
43 changes: 43 additions & 0 deletions src/main/kotlin/org/onflow/flow/sdk/impl/FlowAccessApiImpl.kt
Original file line number Diff line number Diff line change
Expand Up @@ -335,4 +335,47 @@ class FlowAccessApiImpl(
FlowAccessApi.AccessApiCallResponse.Error("Failed to get latest protocol state snapshot", e)
}
}

override fun getTransactionsByBlockId(id: FlowId): FlowAccessApi.AccessApiCallResponse<List<FlowTransaction>> {
return try {
val ret = api.getTransactionsByBlockID(
Access.GetTransactionsByBlockIDRequest.newBuilder()
.setBlockId(id.byteStringValue)
.build()
)
FlowAccessApi.AccessApiCallResponse.Success(ret.transactionsList.map { FlowTransaction.of(it) })
} catch (e: Exception) {
FlowAccessApi.AccessApiCallResponse.Error("Failed to get transactions by block ID", e)
}
}

override fun getTransactionResultsByBlockId(id: FlowId): FlowAccessApi.AccessApiCallResponse<List<FlowTransactionResult>> {
return try {
val ret = api.getTransactionResultsByBlockID(
Access.GetTransactionsByBlockIDRequest.newBuilder()
.setBlockId(id.byteStringValue)
.build()
)
FlowAccessApi.AccessApiCallResponse.Success(ret.transactionResultsList.map { FlowTransactionResult.of(it) })
} catch (e: Exception) {
FlowAccessApi.AccessApiCallResponse.Error("Failed to get transaction results by block ID", e)
}
}

override fun getExecutionResultByBlockId(id: FlowId): FlowAccessApi.AccessApiCallResponse<FlowExecutionResult> {
return try {
val ret = api.getExecutionResultByID(
Access.GetExecutionResultByIDRequest.newBuilder()
.setId(id.byteStringValue)
.build()
)
if (ret.hasExecutionResult()) {
FlowAccessApi.AccessApiCallResponse.Success(FlowExecutionResult.of(ret))
} else {
FlowAccessApi.AccessApiCallResponse.Error("Execution result not found")
}
} catch (e: Exception) {
FlowAccessApi.AccessApiCallResponse.Error("Failed to get execution result by block ID", e)
}
}
}
104 changes: 104 additions & 0 deletions src/main/kotlin/org/onflow/flow/sdk/models.kt
Original file line number Diff line number Diff line change
Expand Up @@ -675,6 +675,110 @@ data class FlowBlock(
}
}

data class FlowChunk(
val collectionIndex: Int,
val startState: ByteArray,
val eventCollection: ByteArray,
val blockId: FlowId,
val totalComputationUsed: Long,
val numberOfTransactions: Int,
val index: Long,
val endState: ByteArray,
val executionDataId: FlowId,
val stateDeltaCommitment: ByteArray,
) : Serializable {
companion object {
fun of(grpcExecutionResult: ExecutionResultOuterClass.Chunk) = FlowChunk(
collectionIndex = grpcExecutionResult.collectionIndex,
startState = grpcExecutionResult.startState.toByteArray(),
eventCollection = grpcExecutionResult.eventCollection.toByteArray(),
blockId = FlowId.of(grpcExecutionResult.blockId.toByteArray()),
totalComputationUsed = grpcExecutionResult.totalComputationUsed,
numberOfTransactions = grpcExecutionResult.numberOfTransactions,
index = grpcExecutionResult.index,
endState = grpcExecutionResult.endState.toByteArray(),
executionDataId = FlowId.of(grpcExecutionResult.executionDataId.toByteArray()),
stateDeltaCommitment = grpcExecutionResult.stateDeltaCommitment.toByteArray()
)
}

override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is FlowChunk) return false

if (collectionIndex != other.collectionIndex) return false
if (!startState.contentEquals(other.startState)) return false
if (!eventCollection.contentEquals(other.eventCollection)) return false
if (blockId != other.blockId) return false
if (totalComputationUsed != other.totalComputationUsed) return false
if (numberOfTransactions != other.numberOfTransactions) return false
if (index != other.index) return false
if (!endState.contentEquals(other.endState)) return false
if (executionDataId != other.executionDataId) return false
if (!stateDeltaCommitment.contentEquals(other.stateDeltaCommitment)) return false

return true
}

override fun hashCode(): Int {
var result = collectionIndex
result = 31 * result + startState.contentHashCode()
result = 31 * result + eventCollection.contentHashCode()
result = 31 * result + blockId.hashCode()
result = 31 * result + totalComputationUsed.hashCode()
result = 31 * result + numberOfTransactions
result = 31 * result + index.hashCode()
result = 31 * result + endState.contentHashCode()
result = 31 * result + executionDataId.hashCode()
result = 31 * result + stateDeltaCommitment.contentHashCode()
return result
}
}

data class FlowServiceEvent(
val type: String,
val payload: ByteArray,
) : Serializable {
companion object {
fun of(grpcExecutionResult: ExecutionResultOuterClass.ServiceEvent) = FlowServiceEvent(
type = grpcExecutionResult.type,
payload = grpcExecutionResult.payload.toByteArray(),
)
}

override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is FlowServiceEvent) return false

if (type != other.type) return false
if (!payload.contentEquals(other.payload)) return false

return true
}

override fun hashCode(): Int {
var result = type.hashCode()
result = 31 * result + payload.contentHashCode()
return result
}
}

data class FlowExecutionResult(
val blockId: FlowId,
val previousResultId: FlowId,
val chunks: List<FlowChunk>,
val serviceEvents: List<FlowServiceEvent>,
) : Serializable {
companion object {
fun of(grpcExecutionResult: Access.ExecutionResultByIDResponse) = FlowExecutionResult(
blockId = FlowId.of(grpcExecutionResult.executionResult.blockId.toByteArray()),
previousResultId = FlowId.of(grpcExecutionResult.executionResult.previousResultId.toByteArray()),
chunks = grpcExecutionResult.executionResult.chunksList.map { FlowChunk.of(it) },
serviceEvents = grpcExecutionResult.executionResult.serviceEventsList.map { FlowServiceEvent.of(it) },
)
}
}

data class FlowCollectionGuarantee(
val id: FlowId,
val signatures: List<FlowSignature>
Expand Down
80 changes: 80 additions & 0 deletions src/test/kotlin/org/onflow/flow/sdk/FlowAccessApiTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -243,4 +243,84 @@ class FlowAccessApiTest {

assertEquals(FlowAccessApi.AccessApiCallResponse.Success(snapshot), result)
}

@Test
fun `Test getTransactionsByBlockId`() {
val flowAccessApi = mock(FlowAccessApi::class.java)
val blockId = FlowId("01")
val transactions = listOf(FlowTransaction.of(TransactionOuterClass.Transaction.getDefaultInstance()))
`when`(flowAccessApi.getTransactionsByBlockId(blockId)).thenReturn(FlowAccessApi.AccessApiCallResponse.Success(transactions))

val result = flowAccessApi.getTransactionsByBlockId(blockId)

assertEquals(FlowAccessApi.AccessApiCallResponse.Success(transactions), result)
}

@Test
fun `Test getTransactionsByBlockId with multiple results`() {
val flowAccessApi = mock(FlowAccessApi::class.java)
val blockId = FlowId("01")

val transaction1 = FlowTransaction(FlowScript("script1"), emptyList(), FlowId.of("01".toByteArray()), 123L, FlowTransactionProposalKey(FlowAddress("02"), 1, 123L), FlowAddress("02"), emptyList())

val transaction2 = FlowTransaction(FlowScript("script2"), emptyList(), FlowId.of("02".toByteArray()), 456L, FlowTransactionProposalKey(FlowAddress("03"), 2, 456L), FlowAddress("03"), emptyList())

val transactions = listOf(transaction1, transaction2)

`when`(flowAccessApi.getTransactionsByBlockId(blockId)).thenReturn(FlowAccessApi.AccessApiCallResponse.Success(transactions))

val result = flowAccessApi.getTransactionsByBlockId(blockId)

assertEquals(FlowAccessApi.AccessApiCallResponse.Success(transactions), result)

assertEquals(2, transactions.size)
assertEquals(transaction1, transactions[0])
assertEquals(transaction2, transactions[1])
}

@Test
fun `Test getTransactionResultsByBlockId`() {
val flowAccessApi = mock(FlowAccessApi::class.java)
val blockId = FlowId("01")
val transactionResults = listOf(FlowTransactionResult.of(Access.TransactionResultResponse.getDefaultInstance()))
`when`(flowAccessApi.getTransactionResultsByBlockId(blockId)).thenReturn(FlowAccessApi.AccessApiCallResponse.Success(transactionResults))

val result = flowAccessApi.getTransactionResultsByBlockId(blockId)

assertEquals(FlowAccessApi.AccessApiCallResponse.Success(transactionResults), result)
}

@Test
fun `Test getTransactionResultsByBlockId with multiple results`() {
val flowAccessApi = mock(FlowAccessApi::class.java)
val blockId = FlowId("01")

val transactionResult1 = FlowTransactionResult(FlowTransactionStatus.SEALED, 1, "message1", emptyList())

val transactionResult2 = FlowTransactionResult(FlowTransactionStatus.SEALED, 2, "message2", emptyList())

val transactions = listOf(transactionResult1, transactionResult2)

`when`(flowAccessApi.getTransactionResultsByBlockId(blockId)).thenReturn(FlowAccessApi.AccessApiCallResponse.Success(transactions))

val result = flowAccessApi.getTransactionResultsByBlockId(blockId)

assertEquals(FlowAccessApi.AccessApiCallResponse.Success(transactions), result)

assertEquals(2, FlowAccessApi.AccessApiCallResponse.Success(transactions).data.size)
assertEquals(transactionResult1, FlowAccessApi.AccessApiCallResponse.Success(transactions).data[0])
assertEquals(transactionResult2, FlowAccessApi.AccessApiCallResponse.Success(transactions).data[1])
}

@Test
fun `Test getExecutionResultByBlockId`() {
val flowAccessApi = mock(FlowAccessApi::class.java)
val blockId = FlowId("01")
val executionResult = FlowExecutionResult.of(Access.ExecutionResultByIDResponse.getDefaultInstance())
`when`(flowAccessApi.getExecutionResultByBlockId(blockId)).thenReturn(FlowAccessApi.AccessApiCallResponse.Success(executionResult))

val result = flowAccessApi.getExecutionResultByBlockId(blockId)

assertEquals(FlowAccessApi.AccessApiCallResponse.Success(executionResult), result)
}
}
Loading
Loading