mirror of
https://github.com/logos-blockchain/lssa.git
synced 2026-05-12 04:40:04 +00:00
Merge pull request #456 from logos-blockchain/Pravdyvy/indexer-query-api
Indexer query API
This commit is contained in:
commit
fda862f5bc
@ -14,6 +14,8 @@ ignore = [
|
||||
{ id = "RUSTSEC-2025-0141", reason = "`bincode` is unmaintained but continuing to use it." },
|
||||
{ id = "RUSTSEC-2023-0089", reason = "atomic-polyfill is pulled transitively via risc0-zkvm; waiting on upstream fix (see https://github.com/risc0/risc0/issues/3453)" },
|
||||
{ id = "RUSTSEC-2026-0097", reason = "`rand` v0.8.5 is present transitively from logos crates, modification may break integration" },
|
||||
{ id = "RUSTSEC-2026-0118", reason = "`hickory-proto` v0.25.0-alpha.5 is present transitively from logos crates, modification may break integration"},
|
||||
{ id = "RUSTSEC-2026-0119", reason = "`hickory-proto` v0.25.0-alpha.5 is present transitively from logos crates, modification may break integration"},
|
||||
]
|
||||
yanked = "deny"
|
||||
unused-ignored-advisory = "deny"
|
||||
|
||||
19
Cargo.lock
generated
19
Cargo.lock
generated
@ -2064,7 +2064,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ab67060fc6b8ef687992d439ca0fa36e7ed17e9a0b16b25b601e8757df720de"
|
||||
dependencies = [
|
||||
"data-encoding",
|
||||
"syn 2.0.117",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2558,7 +2558,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3797,10 +3797,16 @@ dependencies = [
|
||||
name = "indexer_ffi"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cbindgen",
|
||||
"indexer_service",
|
||||
"indexer_service_protocol",
|
||||
"indexer_service_rpc",
|
||||
"jsonrpsee",
|
||||
"log",
|
||||
"nssa",
|
||||
"tokio",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3916,6 +3922,7 @@ dependencies = [
|
||||
"hex",
|
||||
"indexer_ffi",
|
||||
"indexer_service",
|
||||
"indexer_service_protocol",
|
||||
"indexer_service_rpc",
|
||||
"jsonrpsee",
|
||||
"key_protocol",
|
||||
@ -6335,7 +6342,7 @@ version = "0.50.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
|
||||
dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -8120,7 +8127,7 @@ dependencies = [
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -9089,7 +9096,7 @@ dependencies = [
|
||||
"getrandom 0.4.2",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -10375,7 +10382,7 @@ version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
|
||||
dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@ -37,7 +37,7 @@ members = [
|
||||
"examples/program_deployment/methods",
|
||||
"examples/program_deployment/methods/guest",
|
||||
"testnet_initial_state",
|
||||
"indexer_ffi",
|
||||
"indexer/ffi",
|
||||
]
|
||||
|
||||
[workspace.dependencies]
|
||||
@ -57,7 +57,7 @@ indexer_service_protocol = { path = "indexer/service/protocol" }
|
||||
indexer_service_rpc = { path = "indexer/service/rpc" }
|
||||
wallet = { path = "wallet" }
|
||||
wallet-ffi = { path = "wallet-ffi", default-features = false }
|
||||
indexer_ffi = { path = "indexer_ffi" }
|
||||
indexer_ffi = { path = "indexer/ffi" }
|
||||
clock_core = { path = "programs/clock/core" }
|
||||
token_core = { path = "programs/token/core" }
|
||||
token_program = { path = "programs/token" }
|
||||
|
||||
@ -5,9 +5,16 @@ name = "indexer_ffi"
|
||||
version = "0.1.0"
|
||||
|
||||
[dependencies]
|
||||
nssa.workspace = true
|
||||
indexer_service.workspace = true
|
||||
indexer_service_rpc = { workspace = true, features = ["client"] }
|
||||
indexer_service_protocol.workspace = true
|
||||
|
||||
url.workspace = true
|
||||
log = { workspace = true }
|
||||
tokio = { features = ["rt-multi-thread"], workspace = true }
|
||||
jsonrpsee.workspace = true
|
||||
anyhow.workspace = true
|
||||
|
||||
[build-dependencies]
|
||||
cbindgen = "0.29"
|
||||
685
indexer/ffi/indexer_ffi.h
Normal file
685
indexer/ffi/indexer_ffi.h
Normal file
@ -0,0 +1,685 @@
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef enum OperationStatus {
|
||||
Ok = 0,
|
||||
NullPointer = 1,
|
||||
InitializationError = 2,
|
||||
ClientError = 3,
|
||||
} OperationStatus;
|
||||
|
||||
typedef enum FfiTransactionKind {
|
||||
Public = 0,
|
||||
Private,
|
||||
ProgramDeploy,
|
||||
} FfiTransactionKind;
|
||||
|
||||
typedef enum FfiBedrockStatus {
|
||||
Pending = 0,
|
||||
Safe,
|
||||
Finalized,
|
||||
} FfiBedrockStatus;
|
||||
|
||||
typedef struct IndexerServiceFFI {
|
||||
void *indexer_handle;
|
||||
void *runtime;
|
||||
void *indexer_client;
|
||||
} IndexerServiceFFI;
|
||||
|
||||
/**
|
||||
* Simple wrapper around a pointer to a value or an error.
|
||||
*
|
||||
* Pointer is not guaranteed. You should check the error field before
|
||||
* dereferencing the pointer.
|
||||
*/
|
||||
typedef struct PointerResult_IndexerServiceFFI__OperationStatus {
|
||||
struct IndexerServiceFFI *value;
|
||||
enum OperationStatus error;
|
||||
} PointerResult_IndexerServiceFFI__OperationStatus;
|
||||
|
||||
typedef struct PointerResult_IndexerServiceFFI__OperationStatus InitializedIndexerServiceFFIResult;
|
||||
|
||||
/**
|
||||
* Simple wrapper around a pointer to a value or an error.
|
||||
*
|
||||
* Pointer is not guaranteed. You should check the error field before
|
||||
* dereferencing the pointer.
|
||||
*/
|
||||
typedef struct PointerResult_u64__OperationStatus {
|
||||
uint64_t *value;
|
||||
enum OperationStatus error;
|
||||
} PointerResult_u64__OperationStatus;
|
||||
|
||||
typedef uint64_t FfiBlockId;
|
||||
|
||||
/**
|
||||
* 32-byte array type for `AccountId`, keys, hashes, etc.
|
||||
*/
|
||||
typedef struct FfiBytes32 {
|
||||
uint8_t data[32];
|
||||
} FfiBytes32;
|
||||
|
||||
typedef struct FfiBytes32 FfiHashType;
|
||||
|
||||
typedef uint64_t FfiTimestamp;
|
||||
|
||||
/**
|
||||
* 64-byte array type for signatures, etc.
|
||||
*/
|
||||
typedef struct FfiBytes64 {
|
||||
uint8_t data[64];
|
||||
} FfiBytes64;
|
||||
|
||||
typedef struct FfiBytes64 FfiSignature;
|
||||
|
||||
typedef struct FfiBlockHeader {
|
||||
FfiBlockId block_id;
|
||||
FfiHashType prev_block_hash;
|
||||
FfiHashType hash;
|
||||
FfiTimestamp timestamp;
|
||||
FfiSignature signature;
|
||||
} FfiBlockHeader;
|
||||
|
||||
/**
|
||||
* Program ID - 8 u32 values (32 bytes total).
|
||||
*/
|
||||
typedef struct FfiProgramId {
|
||||
uint32_t data[8];
|
||||
} FfiProgramId;
|
||||
|
||||
typedef struct FfiBytes32 FfiAccountId;
|
||||
|
||||
typedef struct FfiVec_FfiAccountId {
|
||||
FfiAccountId *entries;
|
||||
uintptr_t len;
|
||||
uintptr_t capacity;
|
||||
} FfiVec_FfiAccountId;
|
||||
|
||||
typedef struct FfiVec_FfiAccountId FfiAccountIdList;
|
||||
|
||||
/**
|
||||
* U128 - 16 bytes little endian.
|
||||
*/
|
||||
typedef struct FfiU128 {
|
||||
uint8_t data[16];
|
||||
} FfiU128;
|
||||
|
||||
typedef struct FfiU128 FfiNonce;
|
||||
|
||||
typedef struct FfiVec_FfiNonce {
|
||||
FfiNonce *entries;
|
||||
uintptr_t len;
|
||||
uintptr_t capacity;
|
||||
} FfiVec_FfiNonce;
|
||||
|
||||
typedef struct FfiVec_FfiNonce FfiNonceList;
|
||||
|
||||
typedef struct FfiVec_u32 {
|
||||
uint32_t *entries;
|
||||
uintptr_t len;
|
||||
uintptr_t capacity;
|
||||
} FfiVec_u32;
|
||||
|
||||
typedef struct FfiVec_u32 FfiInstructionDataList;
|
||||
|
||||
typedef struct FfiPublicMessage {
|
||||
struct FfiProgramId program_id;
|
||||
FfiAccountIdList account_ids;
|
||||
FfiNonceList nonces;
|
||||
FfiInstructionDataList instruction_data;
|
||||
} FfiPublicMessage;
|
||||
|
||||
typedef struct FfiBytes32 FfiPublicKey;
|
||||
|
||||
typedef struct FfiSignaturePubKeyEntry {
|
||||
FfiSignature signature;
|
||||
FfiPublicKey public_key;
|
||||
} FfiSignaturePubKeyEntry;
|
||||
|
||||
typedef struct FfiVec_FfiSignaturePubKeyEntry {
|
||||
struct FfiSignaturePubKeyEntry *entries;
|
||||
uintptr_t len;
|
||||
uintptr_t capacity;
|
||||
} FfiVec_FfiSignaturePubKeyEntry;
|
||||
|
||||
typedef struct FfiVec_FfiSignaturePubKeyEntry FfiSignaturePubKeyList;
|
||||
|
||||
typedef struct FfiPublicTransactionBody {
|
||||
FfiHashType hash;
|
||||
struct FfiPublicMessage message;
|
||||
FfiSignaturePubKeyList witness_set;
|
||||
} FfiPublicTransactionBody;
|
||||
|
||||
/**
|
||||
* Account data structure - C-compatible version of nssa Account.
|
||||
*
|
||||
* Note: `balance` and `nonce` are u128 values represented as little-endian
|
||||
* byte arrays since C doesn't have native u128 support.
|
||||
*/
|
||||
typedef struct FfiAccount {
|
||||
struct FfiProgramId program_owner;
|
||||
/**
|
||||
* Balance as little-endian [u8; 16].
|
||||
*/
|
||||
struct FfiU128 balance;
|
||||
/**
|
||||
* Pointer to account data bytes.
|
||||
*/
|
||||
uint8_t *data;
|
||||
/**
|
||||
* Length of account data.
|
||||
*/
|
||||
uintptr_t data_len;
|
||||
/**
|
||||
* Capacity of account data.
|
||||
*/
|
||||
uintptr_t data_cap;
|
||||
/**
|
||||
* Nonce as little-endian [u8; 16].
|
||||
*/
|
||||
struct FfiU128 nonce;
|
||||
} FfiAccount;
|
||||
|
||||
typedef struct FfiVec_FfiAccount {
|
||||
struct FfiAccount *entries;
|
||||
uintptr_t len;
|
||||
uintptr_t capacity;
|
||||
} FfiVec_FfiAccount;
|
||||
|
||||
typedef struct FfiVec_FfiAccount FfiAccountList;
|
||||
|
||||
typedef struct FfiVec_u8 {
|
||||
uint8_t *entries;
|
||||
uintptr_t len;
|
||||
uintptr_t capacity;
|
||||
} FfiVec_u8;
|
||||
|
||||
typedef struct FfiVec_u8 FfiVecU8;
|
||||
|
||||
typedef struct FfiEncryptedAccountData {
|
||||
FfiVecU8 ciphertext;
|
||||
FfiVecU8 epk;
|
||||
uint8_t view_tag;
|
||||
} FfiEncryptedAccountData;
|
||||
|
||||
typedef struct FfiVec_FfiEncryptedAccountData {
|
||||
struct FfiEncryptedAccountData *entries;
|
||||
uintptr_t len;
|
||||
uintptr_t capacity;
|
||||
} FfiVec_FfiEncryptedAccountData;
|
||||
|
||||
typedef struct FfiVec_FfiEncryptedAccountData FfiEncryptedAccountDataList;
|
||||
|
||||
typedef struct FfiVec_FfiBytes32 {
|
||||
struct FfiBytes32 *entries;
|
||||
uintptr_t len;
|
||||
uintptr_t capacity;
|
||||
} FfiVec_FfiBytes32;
|
||||
|
||||
typedef struct FfiVec_FfiBytes32 FfiVecBytes32;
|
||||
|
||||
typedef struct FfiNullifierCommitmentSet {
|
||||
struct FfiBytes32 nullifier;
|
||||
struct FfiBytes32 commitment_set_digest;
|
||||
} FfiNullifierCommitmentSet;
|
||||
|
||||
typedef struct FfiVec_FfiNullifierCommitmentSet {
|
||||
struct FfiNullifierCommitmentSet *entries;
|
||||
uintptr_t len;
|
||||
uintptr_t capacity;
|
||||
} FfiVec_FfiNullifierCommitmentSet;
|
||||
|
||||
typedef struct FfiVec_FfiNullifierCommitmentSet FfiNullifierCommitmentSetList;
|
||||
|
||||
typedef struct FfiPrivacyPreservingMessage {
|
||||
FfiAccountIdList public_account_ids;
|
||||
FfiNonceList nonces;
|
||||
FfiAccountList public_post_states;
|
||||
FfiEncryptedAccountDataList encrypted_private_post_states;
|
||||
FfiVecBytes32 new_commitments;
|
||||
FfiNullifierCommitmentSetList new_nullifiers;
|
||||
uint64_t block_validity_window[2];
|
||||
uint64_t timestamp_validity_window[2];
|
||||
} FfiPrivacyPreservingMessage;
|
||||
|
||||
typedef FfiVecU8 FfiProof;
|
||||
|
||||
typedef struct FfiPrivateTransactionBody {
|
||||
FfiHashType hash;
|
||||
struct FfiPrivacyPreservingMessage message;
|
||||
FfiSignaturePubKeyList witness_set;
|
||||
FfiProof proof;
|
||||
} FfiPrivateTransactionBody;
|
||||
|
||||
typedef FfiVecU8 FfiProgramDeploymentMessage;
|
||||
|
||||
typedef struct FfiProgramDeploymentTransactionBody {
|
||||
FfiHashType hash;
|
||||
FfiProgramDeploymentMessage message;
|
||||
} FfiProgramDeploymentTransactionBody;
|
||||
|
||||
typedef struct FfiTransactionBody {
|
||||
struct FfiPublicTransactionBody *public_body;
|
||||
struct FfiPrivateTransactionBody *private_body;
|
||||
struct FfiProgramDeploymentTransactionBody *program_deployment_body;
|
||||
} FfiTransactionBody;
|
||||
|
||||
typedef struct FfiTransaction {
|
||||
struct FfiTransactionBody body;
|
||||
enum FfiTransactionKind kind;
|
||||
} FfiTransaction;
|
||||
|
||||
typedef struct FfiVec_FfiTransaction {
|
||||
struct FfiTransaction *entries;
|
||||
uintptr_t len;
|
||||
uintptr_t capacity;
|
||||
} FfiVec_FfiTransaction;
|
||||
|
||||
typedef struct FfiVec_FfiTransaction FfiBlockBody;
|
||||
|
||||
typedef struct FfiBytes32 FfiMsgId;
|
||||
|
||||
typedef struct FfiBlock {
|
||||
struct FfiBlockHeader header;
|
||||
FfiBlockBody body;
|
||||
enum FfiBedrockStatus bedrock_status;
|
||||
FfiMsgId bedrock_parent_id;
|
||||
} FfiBlock;
|
||||
|
||||
typedef struct FfiOption_FfiBlock {
|
||||
struct FfiBlock *value;
|
||||
bool is_some;
|
||||
} FfiOption_FfiBlock;
|
||||
|
||||
typedef struct FfiOption_FfiBlock FfiBlockOpt;
|
||||
|
||||
/**
|
||||
* Simple wrapper around a pointer to a value or an error.
|
||||
*
|
||||
* Pointer is not guaranteed. You should check the error field before
|
||||
* dereferencing the pointer.
|
||||
*/
|
||||
typedef struct PointerResult_FfiBlockOpt__OperationStatus {
|
||||
FfiBlockOpt *value;
|
||||
enum OperationStatus error;
|
||||
} PointerResult_FfiBlockOpt__OperationStatus;
|
||||
|
||||
/**
|
||||
* Simple wrapper around a pointer to a value or an error.
|
||||
*
|
||||
* Pointer is not guaranteed. You should check the error field before
|
||||
* dereferencing the pointer.
|
||||
*/
|
||||
typedef struct PointerResult_FfiAccount__OperationStatus {
|
||||
struct FfiAccount *value;
|
||||
enum OperationStatus error;
|
||||
} PointerResult_FfiAccount__OperationStatus;
|
||||
|
||||
typedef struct FfiOption_FfiTransaction {
|
||||
struct FfiTransaction *value;
|
||||
bool is_some;
|
||||
} FfiOption_FfiTransaction;
|
||||
|
||||
/**
|
||||
* Simple wrapper around a pointer to a value or an error.
|
||||
*
|
||||
* Pointer is not guaranteed. You should check the error field before
|
||||
* dereferencing the pointer.
|
||||
*/
|
||||
typedef struct PointerResult_FfiOption_FfiTransaction_____OperationStatus {
|
||||
struct FfiOption_FfiTransaction *value;
|
||||
enum OperationStatus error;
|
||||
} PointerResult_FfiOption_FfiTransaction_____OperationStatus;
|
||||
|
||||
typedef struct FfiVec_FfiBlock {
|
||||
struct FfiBlock *entries;
|
||||
uintptr_t len;
|
||||
uintptr_t capacity;
|
||||
} FfiVec_FfiBlock;
|
||||
|
||||
/**
|
||||
* Simple wrapper around a pointer to a value or an error.
|
||||
*
|
||||
* Pointer is not guaranteed. You should check the error field before
|
||||
* dereferencing the pointer.
|
||||
*/
|
||||
typedef struct PointerResult_FfiVec_FfiBlock_____OperationStatus {
|
||||
struct FfiVec_FfiBlock *value;
|
||||
enum OperationStatus error;
|
||||
} PointerResult_FfiVec_FfiBlock_____OperationStatus;
|
||||
|
||||
typedef struct FfiOption_u64 {
|
||||
uint64_t *value;
|
||||
bool is_some;
|
||||
} FfiOption_u64;
|
||||
|
||||
/**
|
||||
* Simple wrapper around a pointer to a value or an error.
|
||||
*
|
||||
* Pointer is not guaranteed. You should check the error field before
|
||||
* dereferencing the pointer.
|
||||
*/
|
||||
typedef struct PointerResult_FfiVec_FfiTransaction_____OperationStatus {
|
||||
struct FfiVec_FfiTransaction *value;
|
||||
enum OperationStatus error;
|
||||
} PointerResult_FfiVec_FfiTransaction_____OperationStatus;
|
||||
|
||||
/**
|
||||
* Creates and starts an indexer based on the provided
|
||||
* configuration file path.
|
||||
*
|
||||
* # Arguments
|
||||
*
|
||||
* - `config_path`: A pointer to a string representing the path to the configuration file.
|
||||
* - `port`: Number representing a port, on which indexers RPC will start.
|
||||
*
|
||||
* # Returns
|
||||
*
|
||||
* An `InitializedIndexerServiceFFIResult` containing either a pointer to the
|
||||
* initialized `IndexerServiceFFI` or an error code.
|
||||
*/
|
||||
InitializedIndexerServiceFFIResult start_indexer(const char *config_path, uint16_t port);
|
||||
|
||||
/**
|
||||
* Stops and frees the resources associated with the given indexer service.
|
||||
*
|
||||
* # Arguments
|
||||
*
|
||||
* - `indexer`: A pointer to the `IndexerServiceFFI` instance to be stopped.
|
||||
*
|
||||
* # Returns
|
||||
*
|
||||
* An `OperationStatus` indicating success or failure.
|
||||
*
|
||||
* # Safety
|
||||
*
|
||||
* The caller must ensure that:
|
||||
* - `indexer` is a valid pointer to a `IndexerServiceFFI` instance
|
||||
* - The `IndexerServiceFFI` instance was created by this library
|
||||
* - The pointer will not be used after this function returns
|
||||
*/
|
||||
enum OperationStatus stop_indexer(struct IndexerServiceFFI *indexer);
|
||||
|
||||
/**
|
||||
* # Safety
|
||||
* It's up to the caller to pass a proper pointer, if somehow from c/c++ side
|
||||
* this is called with a type which doesn't come from a returned `CString` it
|
||||
* will cause a segfault.
|
||||
*/
|
||||
void free_cstring(char *block);
|
||||
|
||||
/**
|
||||
* Query the last block id from indexer.
|
||||
*
|
||||
* # Arguments
|
||||
*
|
||||
* - `indexer`: A pointer to the `IndexerServiceFFI` instance to be queried.
|
||||
*
|
||||
* # Returns
|
||||
*
|
||||
* A `PointerResult<u64, OperationStatus>` indicating success or failure.
|
||||
*
|
||||
* # Safety
|
||||
*
|
||||
* The caller must ensure that:
|
||||
* - `indexer` is a valid pointer to a `IndexerServiceFFI` instance
|
||||
*/
|
||||
struct PointerResult_u64__OperationStatus query_last_block(const struct IndexerServiceFFI *indexer);
|
||||
|
||||
/**
|
||||
* Query the block by id from indexer.
|
||||
*
|
||||
* # Arguments
|
||||
*
|
||||
* - `indexer`: A pointer to the `IndexerServiceFFI` instance to be queried.
|
||||
* - `block_id`: `u64` number of block id
|
||||
*
|
||||
* # Returns
|
||||
*
|
||||
* A `PointerResult<FfiBlockOpt, OperationStatus>` indicating success or failure.
|
||||
*
|
||||
* # Safety
|
||||
*
|
||||
* The caller must ensure that:
|
||||
* - `indexer` is a valid pointer to a `IndexerServiceFFI` instance
|
||||
*/
|
||||
struct PointerResult_FfiBlockOpt__OperationStatus query_block(const struct IndexerServiceFFI *indexer,
|
||||
FfiBlockId block_id);
|
||||
|
||||
/**
|
||||
* Query the block by id from indexer.
|
||||
*
|
||||
* # Arguments
|
||||
*
|
||||
* - `indexer`: A pointer to the `IndexerServiceFFI` instance to be queried.
|
||||
* - `hash`: `FfiHashType` - hash of block
|
||||
*
|
||||
* # Returns
|
||||
*
|
||||
* A `PointerResult<FfiBlockOpt, OperationStatus>` indicating success or failure.
|
||||
*
|
||||
* # Safety
|
||||
*
|
||||
* The caller must ensure that:
|
||||
* - `indexer` is a valid pointer to a `IndexerServiceFFI` instance
|
||||
*/
|
||||
struct PointerResult_FfiBlockOpt__OperationStatus query_block_by_hash(const struct IndexerServiceFFI *indexer,
|
||||
FfiHashType hash);
|
||||
|
||||
/**
|
||||
* Query the account by id from indexer.
|
||||
*
|
||||
* # Arguments
|
||||
*
|
||||
* - `indexer`: A pointer to the `IndexerServiceFFI` instance to be queried.
|
||||
* - `account_id`: `FfiAccountId` - id of queried account
|
||||
*
|
||||
* # Returns
|
||||
*
|
||||
* A `PointerResult<FfiAccount, OperationStatus>` indicating success or failure.
|
||||
*
|
||||
* # Safety
|
||||
*
|
||||
* The caller must ensure that:
|
||||
* - `indexer` is a valid pointer to a `IndexerServiceFFI` instance
|
||||
*/
|
||||
struct PointerResult_FfiAccount__OperationStatus query_account(const struct IndexerServiceFFI *indexer,
|
||||
FfiAccountId account_id);
|
||||
|
||||
/**
|
||||
* Query the trasnaction by hash from indexer.
|
||||
*
|
||||
* # Arguments
|
||||
*
|
||||
* - `indexer`: A pointer to the `IndexerServiceFFI` instance to be queried.
|
||||
* - `hash`: `FfiHashType` - hash of transaction
|
||||
*
|
||||
* # Returns
|
||||
*
|
||||
* A `PointerResult<FfiOption<FfiTransaction>, OperationStatus>` indicating success or failure.
|
||||
*
|
||||
* # Safety
|
||||
*
|
||||
* The caller must ensure that:
|
||||
* - `indexer` is a valid pointer to a `IndexerServiceFFI` instance
|
||||
*/
|
||||
struct PointerResult_FfiOption_FfiTransaction_____OperationStatus query_transaction(const struct IndexerServiceFFI *indexer,
|
||||
FfiHashType hash);
|
||||
|
||||
/**
|
||||
* Query the blocks by block range from indexer.
|
||||
*
|
||||
* # Arguments
|
||||
*
|
||||
* - `indexer`: A pointer to the `IndexerServiceFFI` instance to be queried.
|
||||
* - `before`: `FfiOption<u64>` - end block of query
|
||||
* - `limit`: `u64` - number of blocks to query before `before`
|
||||
*
|
||||
* # Returns
|
||||
*
|
||||
* A `PointerResult<FfiVec<FfiBlock>, OperationStatus>` indicating success or failure.
|
||||
*
|
||||
* # Safety
|
||||
*
|
||||
* The caller must ensure that:
|
||||
* - `indexer` is a valid pointer to a `IndexerServiceFFI` instance
|
||||
*/
|
||||
struct PointerResult_FfiVec_FfiBlock_____OperationStatus query_block_vec(const struct IndexerServiceFFI *indexer,
|
||||
struct FfiOption_u64 before,
|
||||
uint64_t limit);
|
||||
|
||||
/**
|
||||
* Query the transactions range by account id from indexer.
|
||||
*
|
||||
* # Arguments
|
||||
*
|
||||
* - `indexer`: A pointer to the `IndexerServiceFFI` instance to be queried.
|
||||
* - `account_id`: `FfiAccountId` - id of queried account
|
||||
* - `offset`: `u64` - first tx id of query
|
||||
* - `limit`: `u64` - number of tx ids to query after `offset`
|
||||
*
|
||||
* # Returns
|
||||
*
|
||||
* A `PointerResult<FfiVec<FfiBlock>, OperationStatus>` indicating success or failure.
|
||||
*
|
||||
* # Safety
|
||||
*
|
||||
* The caller must ensure that:
|
||||
* - `indexer` is a valid pointer to a `IndexerServiceFFI` instance
|
||||
*/
|
||||
struct PointerResult_FfiVec_FfiTransaction_____OperationStatus query_transactions_by_account(const struct IndexerServiceFFI *indexer,
|
||||
FfiAccountId account_id,
|
||||
uint64_t offset,
|
||||
uint64_t limit);
|
||||
|
||||
/**
|
||||
* Frees the resources associated with the given ffi account.
|
||||
*
|
||||
* # Arguments
|
||||
*
|
||||
* - `val`: An instance of `FfiAccount`.
|
||||
*
|
||||
* # Returns
|
||||
*
|
||||
* void.
|
||||
*
|
||||
* # Safety
|
||||
*
|
||||
* The caller must ensure that:
|
||||
* - `val` is a valid instance of `FfiAccount`.
|
||||
*/
|
||||
void free_ffi_account(struct FfiAccount val);
|
||||
|
||||
/**
|
||||
* Frees the resources associated with the given ffi block.
|
||||
*
|
||||
* # Arguments
|
||||
*
|
||||
* - `val`: An instance of `FfiBlock`.
|
||||
*
|
||||
* # Returns
|
||||
*
|
||||
* void.
|
||||
*
|
||||
* # Safety
|
||||
*
|
||||
* The caller must ensure that:
|
||||
* - `val` is a valid instance of `FfiBlock`.
|
||||
*/
|
||||
void free_ffi_block(struct FfiBlock val);
|
||||
|
||||
/**
|
||||
* Frees the resources associated with the given ffi block option.
|
||||
*
|
||||
* # Arguments
|
||||
*
|
||||
* - `val`: An instance of `FfiBlockOpt`.
|
||||
*
|
||||
* # Returns
|
||||
*
|
||||
* void.
|
||||
*
|
||||
* # Safety
|
||||
*
|
||||
* The caller must ensure that:
|
||||
* - `val` is a valid instance of `FfiBlockOpt`.
|
||||
*/
|
||||
void free_ffi_block_opt(FfiBlockOpt val);
|
||||
|
||||
/**
|
||||
* Frees the resources associated with the given ffi block vector.
|
||||
*
|
||||
* # Arguments
|
||||
*
|
||||
* - `val`: An instance of `FfiVec<FfiBlock>`.
|
||||
*
|
||||
* # Returns
|
||||
*
|
||||
* void.
|
||||
*
|
||||
* # Safety
|
||||
*
|
||||
* The caller must ensure that:
|
||||
* - `val` is a valid instance of `FfiVec<FfiBlock>`.
|
||||
*/
|
||||
void free_ffi_block_vec(struct FfiVec_FfiBlock val);
|
||||
|
||||
/**
|
||||
* Frees the resources associated with the given ffi transaction.
|
||||
*
|
||||
* # Arguments
|
||||
*
|
||||
* - `val`: An instance of `FfiTransaction`.
|
||||
*
|
||||
* # Returns
|
||||
*
|
||||
* void.
|
||||
*
|
||||
* # Safety
|
||||
*
|
||||
* The caller must ensure that:
|
||||
* - `val` is a valid instance of `FfiTransaction`.
|
||||
*/
|
||||
void free_ffi_transaction(struct FfiTransaction val);
|
||||
|
||||
/**
|
||||
* Frees the resources associated with the given ffi transaction option.
|
||||
*
|
||||
* # Arguments
|
||||
*
|
||||
* - `val`: An instance of `FfiOption<FfiTransaction>`.
|
||||
*
|
||||
* # Returns
|
||||
*
|
||||
* void.
|
||||
*
|
||||
* # Safety
|
||||
*
|
||||
* The caller must ensure that:
|
||||
* - `val` is a valid instance of `FfiOption<FfiTransaction>`.
|
||||
*/
|
||||
void free_ffi_transaction_opt(struct FfiOption_FfiTransaction val);
|
||||
|
||||
/**
|
||||
* Frees the resources associated with the given vector of ffi transactions.
|
||||
*
|
||||
* # Arguments
|
||||
*
|
||||
* - `val`: An instance of `FfiVec<FfiTransaction>`.
|
||||
*
|
||||
* # Returns
|
||||
*
|
||||
* void.
|
||||
*
|
||||
* # Safety
|
||||
*
|
||||
* The caller must ensure that:
|
||||
* - `val` is a valid instance of `FfiVec<FfiTransaction>`.
|
||||
*/
|
||||
void free_ffi_transaction_vec(struct FfiVec_FfiTransaction val);
|
||||
|
||||
bool is_ok(const enum OperationStatus *self);
|
||||
|
||||
bool is_error(const enum OperationStatus *self);
|
||||
36
indexer/ffi/src/api/client.rs
Normal file
36
indexer/ffi/src/api/client.rs
Normal file
@ -0,0 +1,36 @@
|
||||
use std::net::SocketAddr;
|
||||
|
||||
use url::Url;
|
||||
|
||||
use crate::OperationStatus;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum UrlProtocol {
|
||||
Http,
|
||||
Ws,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for UrlProtocol {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Http => write!(f, "http"),
|
||||
Self::Ws => write!(f, "ws"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn addr_to_url(protocol: UrlProtocol, addr: SocketAddr) -> Result<Url, OperationStatus> {
|
||||
// Convert 0.0.0.0 to 127.0.0.1 for client connections
|
||||
// When binding to port 0, the server binds to 0.0.0.0:<random_port>
|
||||
// but clients need to connect to 127.0.0.1:<port> to work reliably
|
||||
let url_string = if addr.ip().is_unspecified() {
|
||||
format!("{protocol}://127.0.0.1:{}", addr.port())
|
||||
} else {
|
||||
format!("{protocol}://{addr}")
|
||||
};
|
||||
|
||||
url_string.parse().map_err(|e| {
|
||||
log::error!("Could not parse indexer url: {e}");
|
||||
OperationStatus::InitializationError
|
||||
})
|
||||
}
|
||||
@ -2,7 +2,15 @@ use std::{ffi::c_char, path::PathBuf};
|
||||
|
||||
use tokio::runtime::Runtime;
|
||||
|
||||
use crate::{IndexerServiceFFI, api::PointerResult, errors::OperationStatus};
|
||||
use crate::{
|
||||
IndexerServiceFFI,
|
||||
api::{
|
||||
PointerResult,
|
||||
client::{UrlProtocol, addr_to_url},
|
||||
},
|
||||
client::{IndexerClient, IndexerClientTrait as _},
|
||||
errors::OperationStatus,
|
||||
};
|
||||
|
||||
pub type InitializedIndexerServiceFFIResult = PointerResult<IndexerServiceFFI, OperationStatus>;
|
||||
|
||||
@ -67,7 +75,13 @@ fn setup_indexer(
|
||||
OperationStatus::InitializationError
|
||||
})?;
|
||||
|
||||
Ok(IndexerServiceFFI::new(indexer_handle, rt))
|
||||
let indexer_url = addr_to_url(UrlProtocol::Ws, indexer_handle.addr())?;
|
||||
let indexer_client = rt.block_on(IndexerClient::new(&indexer_url)).map_err(|e| {
|
||||
log::error!("Could not start indexer client: {e}");
|
||||
OperationStatus::InitializationError
|
||||
})?;
|
||||
|
||||
Ok(IndexerServiceFFI::new(indexer_handle, rt, indexer_client))
|
||||
}
|
||||
|
||||
/// Stops and frees the resources associated with the given indexer service.
|
||||
@ -1,5 +1,8 @@
|
||||
pub use result::PointerResult;
|
||||
|
||||
pub mod client;
|
||||
pub mod lifecycle;
|
||||
pub mod memory;
|
||||
pub mod query;
|
||||
pub mod result;
|
||||
pub mod types;
|
||||
334
indexer/ffi/src/api/query.rs
Normal file
334
indexer/ffi/src/api/query.rs
Normal file
@ -0,0 +1,334 @@
|
||||
use indexer_service_protocol::{AccountId, HashType};
|
||||
use indexer_service_rpc::RpcClient as _;
|
||||
|
||||
use crate::{
|
||||
IndexerServiceFFI,
|
||||
api::{
|
||||
PointerResult,
|
||||
types::{
|
||||
FfiAccountId, FfiBlockId, FfiHashType, FfiOption, FfiVec,
|
||||
account::FfiAccount,
|
||||
block::{FfiBlock, FfiBlockOpt},
|
||||
transaction::FfiTransaction,
|
||||
},
|
||||
},
|
||||
errors::OperationStatus,
|
||||
};
|
||||
|
||||
/// Query the last block id from indexer.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// - `indexer`: A pointer to the `IndexerServiceFFI` instance to be queried.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// A `PointerResult<u64, OperationStatus>` indicating success or failure.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure that:
|
||||
/// - `indexer` is a valid pointer to a `IndexerServiceFFI` instance
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn query_last_block(
|
||||
indexer: *const IndexerServiceFFI,
|
||||
) -> PointerResult<u64, OperationStatus> {
|
||||
if indexer.is_null() {
|
||||
log::error!("Attempted to query a null indexer pointer. This is a bug. Aborting.");
|
||||
return PointerResult::from_error(OperationStatus::NullPointer);
|
||||
}
|
||||
|
||||
let indexer = unsafe { &*indexer };
|
||||
|
||||
let client = unsafe { indexer.client() };
|
||||
let runtime = unsafe { indexer.runtime() };
|
||||
|
||||
runtime
|
||||
.block_on(client.get_last_finalized_block_id())
|
||||
.map_or_else(
|
||||
|_| PointerResult::from_error(OperationStatus::ClientError),
|
||||
PointerResult::from_value,
|
||||
)
|
||||
}
|
||||
|
||||
/// Query the block by id from indexer.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// - `indexer`: A pointer to the `IndexerServiceFFI` instance to be queried.
|
||||
/// - `block_id`: `u64` number of block id
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// A `PointerResult<FfiBlockOpt, OperationStatus>` indicating success or failure.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure that:
|
||||
/// - `indexer` is a valid pointer to a `IndexerServiceFFI` instance
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn query_block(
|
||||
indexer: *const IndexerServiceFFI,
|
||||
block_id: FfiBlockId,
|
||||
) -> PointerResult<FfiBlockOpt, OperationStatus> {
|
||||
if indexer.is_null() {
|
||||
log::error!("Attempted to query a null indexer pointer. This is a bug. Aborting.");
|
||||
return PointerResult::from_error(OperationStatus::NullPointer);
|
||||
}
|
||||
|
||||
let indexer = unsafe { &*indexer };
|
||||
|
||||
let client = unsafe { indexer.client() };
|
||||
let runtime = unsafe { indexer.runtime() };
|
||||
|
||||
runtime
|
||||
.block_on(client.get_block_by_id(block_id))
|
||||
.map_or_else(
|
||||
|_| PointerResult::from_error(OperationStatus::ClientError),
|
||||
|block_opt| {
|
||||
let block_ffi = block_opt.map_or_else(FfiBlockOpt::from_none, |block| {
|
||||
FfiBlockOpt::from_value(block.into())
|
||||
});
|
||||
|
||||
PointerResult::from_value(block_ffi)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/// Query the block by id from indexer.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// - `indexer`: A pointer to the `IndexerServiceFFI` instance to be queried.
|
||||
/// - `hash`: `FfiHashType` - hash of block
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// A `PointerResult<FfiBlockOpt, OperationStatus>` indicating success or failure.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure that:
|
||||
/// - `indexer` is a valid pointer to a `IndexerServiceFFI` instance
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn query_block_by_hash(
|
||||
indexer: *const IndexerServiceFFI,
|
||||
hash: FfiHashType,
|
||||
) -> PointerResult<FfiBlockOpt, OperationStatus> {
|
||||
if indexer.is_null() {
|
||||
log::error!("Attempted to query a null indexer pointer. This is a bug. Aborting.");
|
||||
return PointerResult::from_error(OperationStatus::NullPointer);
|
||||
}
|
||||
|
||||
let indexer = unsafe { &*indexer };
|
||||
|
||||
let client = unsafe { indexer.client() };
|
||||
let runtime = unsafe { indexer.runtime() };
|
||||
|
||||
runtime
|
||||
.block_on(client.get_block_by_hash(HashType(hash.data)))
|
||||
.map_or_else(
|
||||
|_| PointerResult::from_error(OperationStatus::ClientError),
|
||||
|block_opt| {
|
||||
let block_ffi = block_opt.map_or_else(FfiBlockOpt::from_none, |block| {
|
||||
FfiBlockOpt::from_value(block.into())
|
||||
});
|
||||
|
||||
PointerResult::from_value(block_ffi)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/// Query the account by id from indexer.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// - `indexer`: A pointer to the `IndexerServiceFFI` instance to be queried.
|
||||
/// - `account_id`: `FfiAccountId` - id of queried account
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// A `PointerResult<FfiAccount, OperationStatus>` indicating success or failure.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure that:
|
||||
/// - `indexer` is a valid pointer to a `IndexerServiceFFI` instance
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn query_account(
|
||||
indexer: *const IndexerServiceFFI,
|
||||
account_id: FfiAccountId,
|
||||
) -> PointerResult<FfiAccount, OperationStatus> {
|
||||
if indexer.is_null() {
|
||||
log::error!("Attempted to query a null indexer pointer. This is a bug. Aborting.");
|
||||
return PointerResult::from_error(OperationStatus::NullPointer);
|
||||
}
|
||||
|
||||
let indexer = unsafe { &*indexer };
|
||||
|
||||
let client = unsafe { indexer.client() };
|
||||
let runtime = unsafe { indexer.runtime() };
|
||||
|
||||
runtime
|
||||
.block_on(client.get_account(AccountId {
|
||||
value: account_id.data,
|
||||
}))
|
||||
.map_or_else(
|
||||
|_| PointerResult::from_error(OperationStatus::ClientError),
|
||||
|acc| {
|
||||
let acc_nssa: nssa::Account =
|
||||
acc.try_into().expect("Source is in blocks, must fit");
|
||||
PointerResult::from_value(acc_nssa.into())
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/// Query the trasnaction by hash from indexer.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// - `indexer`: A pointer to the `IndexerServiceFFI` instance to be queried.
|
||||
/// - `hash`: `FfiHashType` - hash of transaction
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// A `PointerResult<FfiOption<FfiTransaction>, OperationStatus>` indicating success or failure.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure that:
|
||||
/// - `indexer` is a valid pointer to a `IndexerServiceFFI` instance
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn query_transaction(
|
||||
indexer: *const IndexerServiceFFI,
|
||||
hash: FfiHashType,
|
||||
) -> PointerResult<FfiOption<FfiTransaction>, OperationStatus> {
|
||||
if indexer.is_null() {
|
||||
log::error!("Attempted to query a null indexer pointer. This is a bug. Aborting.");
|
||||
return PointerResult::from_error(OperationStatus::NullPointer);
|
||||
}
|
||||
|
||||
let indexer = unsafe { &*indexer };
|
||||
|
||||
let client = unsafe { indexer.client() };
|
||||
let runtime = unsafe { indexer.runtime() };
|
||||
|
||||
runtime
|
||||
.block_on(client.get_transaction(HashType(hash.data)))
|
||||
.map_or_else(
|
||||
|_| PointerResult::from_error(OperationStatus::ClientError),
|
||||
|tx_opt| {
|
||||
let tx_ffi = tx_opt.map_or_else(FfiOption::<FfiTransaction>::from_none, |tx| {
|
||||
FfiOption::<FfiTransaction>::from_value(tx.into())
|
||||
});
|
||||
|
||||
PointerResult::from_value(tx_ffi)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/// Query the blocks by block range from indexer.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// - `indexer`: A pointer to the `IndexerServiceFFI` instance to be queried.
|
||||
/// - `before`: `FfiOption<u64>` - end block of query
|
||||
/// - `limit`: `u64` - number of blocks to query before `before`
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// A `PointerResult<FfiVec<FfiBlock>, OperationStatus>` indicating success or failure.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure that:
|
||||
/// - `indexer` is a valid pointer to a `IndexerServiceFFI` instance
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn query_block_vec(
|
||||
indexer: *const IndexerServiceFFI,
|
||||
before: FfiOption<u64>,
|
||||
limit: u64,
|
||||
) -> PointerResult<FfiVec<FfiBlock>, OperationStatus> {
|
||||
if indexer.is_null() {
|
||||
log::error!("Attempted to query a null indexer pointer. This is a bug. Aborting.");
|
||||
return PointerResult::from_error(OperationStatus::NullPointer);
|
||||
}
|
||||
|
||||
let indexer = unsafe { &*indexer };
|
||||
|
||||
let client = unsafe { indexer.client() };
|
||||
let runtime = unsafe { indexer.runtime() };
|
||||
|
||||
let before_std = before.is_some.then(|| unsafe { *before.value });
|
||||
|
||||
runtime
|
||||
.block_on(client.get_blocks(before_std, limit))
|
||||
.map_or_else(
|
||||
|_| PointerResult::from_error(OperationStatus::ClientError),
|
||||
|block_vec| {
|
||||
PointerResult::from_value(
|
||||
block_vec
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.collect::<Vec<_>>()
|
||||
.into(),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/// Query the transactions range by account id from indexer.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// - `indexer`: A pointer to the `IndexerServiceFFI` instance to be queried.
|
||||
/// - `account_id`: `FfiAccountId` - id of queried account
|
||||
/// - `offset`: `u64` - first tx id of query
|
||||
/// - `limit`: `u64` - number of tx ids to query after `offset`
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// A `PointerResult<FfiVec<FfiBlock>, OperationStatus>` indicating success or failure.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure that:
|
||||
/// - `indexer` is a valid pointer to a `IndexerServiceFFI` instance
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn query_transactions_by_account(
|
||||
indexer: *const IndexerServiceFFI,
|
||||
account_id: FfiAccountId,
|
||||
offset: u64,
|
||||
limit: u64,
|
||||
) -> PointerResult<FfiVec<FfiTransaction>, OperationStatus> {
|
||||
if indexer.is_null() {
|
||||
log::error!("Attempted to query a null indexer pointer. This is a bug. Aborting.");
|
||||
return PointerResult::from_error(OperationStatus::NullPointer);
|
||||
}
|
||||
|
||||
let indexer = unsafe { &*indexer };
|
||||
|
||||
let client = unsafe { indexer.client() };
|
||||
let runtime = unsafe { indexer.runtime() };
|
||||
|
||||
runtime
|
||||
.block_on(client.get_transactions_by_account(
|
||||
AccountId {
|
||||
value: account_id.data,
|
||||
},
|
||||
offset,
|
||||
limit,
|
||||
))
|
||||
.map_or_else(
|
||||
|_| PointerResult::from_error(OperationStatus::ClientError),
|
||||
|tx_vec| {
|
||||
PointerResult::from_value(
|
||||
tx_vec
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.collect::<Vec<_>>()
|
||||
.into(),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
119
indexer/ffi/src/api/types/account.rs
Normal file
119
indexer/ffi/src/api/types/account.rs
Normal file
@ -0,0 +1,119 @@
|
||||
use indexer_service_protocol::ProgramId;
|
||||
|
||||
use crate::api::types::{FfiBytes32, FfiProgramId, FfiU128};
|
||||
|
||||
/// Account data structure - C-compatible version of nssa Account.
|
||||
///
|
||||
/// Note: `balance` and `nonce` are u128 values represented as little-endian
|
||||
/// byte arrays since C doesn't have native u128 support.
|
||||
#[repr(C)]
|
||||
pub struct FfiAccount {
|
||||
pub program_owner: FfiProgramId,
|
||||
/// Balance as little-endian [u8; 16].
|
||||
pub balance: FfiU128,
|
||||
/// Pointer to account data bytes.
|
||||
pub data: *mut u8,
|
||||
/// Length of account data.
|
||||
pub data_len: usize,
|
||||
/// Capacity of account data.
|
||||
pub data_cap: usize,
|
||||
/// Nonce as little-endian [u8; 16].
|
||||
pub nonce: FfiU128,
|
||||
}
|
||||
|
||||
// Helper functions to convert between Rust and FFI types
|
||||
|
||||
impl From<&nssa::AccountId> for FfiBytes32 {
|
||||
fn from(id: &nssa::AccountId) -> Self {
|
||||
Self::from_account_id(id)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<nssa::Account> for FfiAccount {
|
||||
fn from(value: nssa::Account) -> Self {
|
||||
let nssa::Account {
|
||||
program_owner,
|
||||
balance,
|
||||
data,
|
||||
nonce,
|
||||
} = value;
|
||||
|
||||
let (data, data_len, data_cap) = data.into_inner().into_raw_parts();
|
||||
|
||||
let program_owner = FfiProgramId {
|
||||
data: program_owner,
|
||||
};
|
||||
Self {
|
||||
program_owner,
|
||||
balance: balance.into(),
|
||||
data,
|
||||
data_len,
|
||||
data_cap,
|
||||
nonce: nonce.0.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<FfiAccount> for indexer_service_protocol::Account {
|
||||
fn from(value: FfiAccount) -> Self {
|
||||
let FfiAccount {
|
||||
program_owner,
|
||||
balance,
|
||||
data,
|
||||
data_cap,
|
||||
data_len,
|
||||
nonce,
|
||||
} = value;
|
||||
|
||||
Self {
|
||||
program_owner: ProgramId(program_owner.data),
|
||||
balance: balance.into(),
|
||||
data: indexer_service_protocol::Data(unsafe {
|
||||
Vec::from_raw_parts(data, data_len, data_cap)
|
||||
}),
|
||||
nonce: nonce.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&FfiAccount> for indexer_service_protocol::Account {
|
||||
fn from(value: &FfiAccount) -> Self {
|
||||
let &FfiAccount {
|
||||
program_owner,
|
||||
balance,
|
||||
data,
|
||||
data_cap,
|
||||
data_len,
|
||||
nonce,
|
||||
} = value;
|
||||
|
||||
Self {
|
||||
program_owner: ProgramId(program_owner.data),
|
||||
balance: balance.into(),
|
||||
data: indexer_service_protocol::Data(unsafe {
|
||||
Vec::from_raw_parts(data, data_len, data_cap)
|
||||
}),
|
||||
nonce: nonce.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Frees the resources associated with the given ffi account.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// - `val`: An instance of `FfiAccount`.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// void.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure that:
|
||||
/// - `val` is a valid instance of `FfiAccount`.
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn free_ffi_account(val: FfiAccount) {
|
||||
let orig_val: indexer_service_protocol::Account = val.into();
|
||||
drop(orig_val);
|
||||
}
|
||||
199
indexer/ffi/src/api/types/block.rs
Normal file
199
indexer/ffi/src/api/types/block.rs
Normal file
@ -0,0 +1,199 @@
|
||||
use indexer_service_protocol::{
|
||||
BedrockStatus, Block, BlockHeader, HashType, MantleMsgId, Signature,
|
||||
};
|
||||
|
||||
use crate::api::types::{
|
||||
FfiBlockId, FfiHashType, FfiMsgId, FfiOption, FfiSignature, FfiTimestamp, FfiVec,
|
||||
transaction::free_ffi_transaction_vec, vectors::FfiBlockBody,
|
||||
};
|
||||
|
||||
#[repr(C)]
|
||||
pub struct FfiBlock {
|
||||
pub header: FfiBlockHeader,
|
||||
pub body: FfiBlockBody,
|
||||
pub bedrock_status: FfiBedrockStatus,
|
||||
pub bedrock_parent_id: FfiMsgId,
|
||||
}
|
||||
|
||||
impl From<Block> for FfiBlock {
|
||||
fn from(value: Block) -> Self {
|
||||
let Block {
|
||||
header,
|
||||
body,
|
||||
bedrock_status,
|
||||
bedrock_parent_id,
|
||||
} = value;
|
||||
|
||||
Self {
|
||||
header: header.into(),
|
||||
body: body
|
||||
.transactions
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.collect::<Vec<_>>()
|
||||
.into(),
|
||||
bedrock_status: bedrock_status.into(),
|
||||
bedrock_parent_id: bedrock_parent_id.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type FfiBlockOpt = FfiOption<FfiBlock>;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct FfiBlockHeader {
|
||||
pub block_id: FfiBlockId,
|
||||
pub prev_block_hash: FfiHashType,
|
||||
pub hash: FfiHashType,
|
||||
pub timestamp: FfiTimestamp,
|
||||
pub signature: FfiSignature,
|
||||
}
|
||||
|
||||
impl From<BlockHeader> for FfiBlockHeader {
|
||||
fn from(value: BlockHeader) -> Self {
|
||||
let BlockHeader {
|
||||
block_id,
|
||||
prev_block_hash,
|
||||
hash,
|
||||
timestamp,
|
||||
signature,
|
||||
} = value;
|
||||
|
||||
Self {
|
||||
block_id,
|
||||
prev_block_hash: prev_block_hash.into(),
|
||||
hash: hash.into(),
|
||||
timestamp,
|
||||
signature: signature.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub enum FfiBedrockStatus {
|
||||
Pending = 0x0,
|
||||
Safe,
|
||||
Finalized,
|
||||
}
|
||||
|
||||
impl From<BedrockStatus> for FfiBedrockStatus {
|
||||
fn from(value: BedrockStatus) -> Self {
|
||||
match value {
|
||||
BedrockStatus::Finalized => Self::Finalized,
|
||||
BedrockStatus::Pending => Self::Pending,
|
||||
BedrockStatus::Safe => Self::Safe,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<FfiBedrockStatus> for BedrockStatus {
|
||||
fn from(value: FfiBedrockStatus) -> Self {
|
||||
match value {
|
||||
FfiBedrockStatus::Finalized => Self::Finalized,
|
||||
FfiBedrockStatus::Pending => Self::Pending,
|
||||
FfiBedrockStatus::Safe => Self::Safe,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Frees the resources associated with the given ffi block.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// - `val`: An instance of `FfiBlock`.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// void.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure that:
|
||||
/// - `val` is a valid instance of `FfiBlock`.
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn free_ffi_block(val: FfiBlock) {
|
||||
// We don't really need all the casts, but just in case
|
||||
// All except `ffi_tx_ffi_vec` is Copy types, so no need for Drop
|
||||
let _ = BlockHeader {
|
||||
block_id: val.header.block_id,
|
||||
prev_block_hash: HashType(val.header.prev_block_hash.data),
|
||||
hash: HashType(val.header.hash.data),
|
||||
timestamp: val.header.timestamp,
|
||||
signature: Signature(val.header.signature.data),
|
||||
};
|
||||
let ffi_tx_ffi_vec = val.body;
|
||||
|
||||
#[expect(clippy::let_underscore_must_use, reason = "No use for this Copy type")]
|
||||
let _: BedrockStatus = val.bedrock_status.into();
|
||||
|
||||
let _ = MantleMsgId(val.bedrock_parent_id.data);
|
||||
|
||||
unsafe {
|
||||
free_ffi_transaction_vec(ffi_tx_ffi_vec);
|
||||
};
|
||||
}
|
||||
|
||||
/// Frees the resources associated with the given ffi block option.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// - `val`: An instance of `FfiBlockOpt`.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// void.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure that:
|
||||
/// - `val` is a valid instance of `FfiBlockOpt`.
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn free_ffi_block_opt(val: FfiBlockOpt) {
|
||||
if val.is_some {
|
||||
let value = unsafe { Box::from_raw(val.value) };
|
||||
|
||||
// We don't really need all the casts, but just in case
|
||||
// All except `ffi_tx_ffi_vec` is Copy types, so no need for Drop
|
||||
let _ = BlockHeader {
|
||||
block_id: value.header.block_id,
|
||||
prev_block_hash: HashType(value.header.prev_block_hash.data),
|
||||
hash: HashType(value.header.hash.data),
|
||||
timestamp: value.header.timestamp,
|
||||
signature: Signature(value.header.signature.data),
|
||||
};
|
||||
let ffi_tx_ffi_vec = value.body;
|
||||
|
||||
#[expect(clippy::let_underscore_must_use, reason = "No use for this Copy type")]
|
||||
let _: BedrockStatus = value.bedrock_status.into();
|
||||
|
||||
let _ = MantleMsgId(value.bedrock_parent_id.data);
|
||||
|
||||
unsafe {
|
||||
free_ffi_transaction_vec(ffi_tx_ffi_vec);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Frees the resources associated with the given ffi block vector.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// - `val`: An instance of `FfiVec<FfiBlock>`.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// void.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure that:
|
||||
/// - `val` is a valid instance of `FfiVec<FfiBlock>`.
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn free_ffi_block_vec(val: FfiVec<FfiBlock>) {
|
||||
let ffi_block_std_vec: Vec<_> = val.into();
|
||||
for block in ffi_block_std_vec {
|
||||
unsafe {
|
||||
free_ffi_block(block);
|
||||
}
|
||||
}
|
||||
}
|
||||
165
indexer/ffi/src/api/types/mod.rs
Normal file
165
indexer/ffi/src/api/types/mod.rs
Normal file
@ -0,0 +1,165 @@
|
||||
use indexer_service_protocol::{AccountId, HashType, MantleMsgId, ProgramId, PublicKey, Signature};
|
||||
|
||||
pub mod account;
|
||||
pub mod block;
|
||||
pub mod transaction;
|
||||
pub mod vectors;
|
||||
|
||||
/// 32-byte array type for `AccountId`, keys, hashes, etc.
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Default)]
|
||||
pub struct FfiBytes32 {
|
||||
pub data: [u8; 32],
|
||||
}
|
||||
|
||||
/// 64-byte array type for signatures, etc.
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct FfiBytes64 {
|
||||
pub data: [u8; 64],
|
||||
}
|
||||
|
||||
/// Program ID - 8 u32 values (32 bytes total).
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Default)]
|
||||
pub struct FfiProgramId {
|
||||
pub data: [u32; 8],
|
||||
}
|
||||
|
||||
impl From<ProgramId> for FfiProgramId {
|
||||
fn from(value: ProgramId) -> Self {
|
||||
Self { data: value.0 }
|
||||
}
|
||||
}
|
||||
|
||||
/// U128 - 16 bytes little endian.
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Default)]
|
||||
pub struct FfiU128 {
|
||||
pub data: [u8; 16],
|
||||
}
|
||||
|
||||
impl FfiBytes32 {
|
||||
/// Create from a 32-byte array.
|
||||
#[must_use]
|
||||
pub const fn from_bytes(bytes: [u8; 32]) -> Self {
|
||||
Self { data: bytes }
|
||||
}
|
||||
|
||||
/// Create from an `AccountId`.
|
||||
#[must_use]
|
||||
pub const fn from_account_id(id: &nssa::AccountId) -> Self {
|
||||
Self { data: *id.value() }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u128> for FfiU128 {
|
||||
fn from(value: u128) -> Self {
|
||||
Self {
|
||||
data: value.to_le_bytes(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<FfiU128> for u128 {
|
||||
fn from(value: FfiU128) -> Self {
|
||||
Self::from_le_bytes(value.data)
|
||||
}
|
||||
}
|
||||
|
||||
pub type FfiHashType = FfiBytes32;
|
||||
pub type FfiMsgId = FfiBytes32;
|
||||
pub type FfiBlockId = u64;
|
||||
pub type FfiTimestamp = u64;
|
||||
pub type FfiSignature = FfiBytes64;
|
||||
pub type FfiAccountId = FfiBytes32;
|
||||
pub type FfiNonce = FfiU128;
|
||||
pub type FfiPublicKey = FfiBytes32;
|
||||
|
||||
impl From<HashType> for FfiHashType {
|
||||
fn from(value: HashType) -> Self {
|
||||
Self { data: value.0 }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MantleMsgId> for FfiMsgId {
|
||||
fn from(value: MantleMsgId) -> Self {
|
||||
Self { data: value.0 }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Signature> for FfiSignature {
|
||||
fn from(value: Signature) -> Self {
|
||||
Self { data: value.0 }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<AccountId> for FfiAccountId {
|
||||
fn from(value: AccountId) -> Self {
|
||||
Self { data: value.value }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PublicKey> for FfiPublicKey {
|
||||
fn from(value: PublicKey) -> Self {
|
||||
Self { data: value.0 }
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct FfiVec<T> {
|
||||
pub entries: *mut T,
|
||||
pub len: usize,
|
||||
pub capacity: usize,
|
||||
}
|
||||
|
||||
impl<T> From<Vec<T>> for FfiVec<T> {
|
||||
fn from(value: Vec<T>) -> Self {
|
||||
let (entries, len, capacity) = value.into_raw_parts();
|
||||
Self {
|
||||
entries,
|
||||
len,
|
||||
capacity,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<FfiVec<T>> for Vec<T> {
|
||||
fn from(value: FfiVec<T>) -> Self {
|
||||
unsafe { Self::from_raw_parts(value.entries, value.len, value.capacity) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> FfiVec<T> {
|
||||
/// # Safety
|
||||
///
|
||||
/// `index` must be lesser than `self.len`.
|
||||
#[must_use]
|
||||
pub unsafe fn get(&self, index: usize) -> &T {
|
||||
let ptr = unsafe { self.entries.add(index) };
|
||||
unsafe { &*ptr }
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct FfiOption<T> {
|
||||
pub value: *mut T,
|
||||
pub is_some: bool,
|
||||
}
|
||||
|
||||
impl<T> FfiOption<T> {
|
||||
pub fn from_value(val: T) -> Self {
|
||||
Self {
|
||||
value: Box::into_raw(Box::new(val)),
|
||||
is_some: true,
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn from_none() -> Self {
|
||||
Self {
|
||||
value: std::ptr::null_mut(),
|
||||
is_some: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
548
indexer/ffi/src/api/types/transaction.rs
Normal file
548
indexer/ffi/src/api/types/transaction.rs
Normal file
@ -0,0 +1,548 @@
|
||||
use indexer_service_protocol::{
|
||||
AccountId, Ciphertext, Commitment, CommitmentSetDigest, EncryptedAccountData,
|
||||
EphemeralPublicKey, HashType, Nullifier, PrivacyPreservingMessage,
|
||||
PrivacyPreservingTransaction, ProgramDeploymentMessage, ProgramDeploymentTransaction,
|
||||
ProgramId, Proof, PublicKey, PublicMessage, PublicTransaction, Signature, Transaction,
|
||||
ValidityWindow, WitnessSet,
|
||||
};
|
||||
|
||||
use crate::api::types::{
|
||||
FfiBytes32, FfiHashType, FfiOption, FfiProgramId, FfiPublicKey, FfiSignature, FfiVec,
|
||||
vectors::{
|
||||
FfiAccountIdList, FfiAccountList, FfiEncryptedAccountDataList, FfiInstructionDataList,
|
||||
FfiNonceList, FfiNullifierCommitmentSetList, FfiProgramDeploymentMessage, FfiProof,
|
||||
FfiSignaturePubKeyList, FfiVecBytes32, FfiVecU8,
|
||||
},
|
||||
};
|
||||
|
||||
#[repr(C)]
|
||||
pub struct FfiPublicTransactionBody {
|
||||
pub hash: FfiHashType,
|
||||
pub message: FfiPublicMessage,
|
||||
pub witness_set: FfiSignaturePubKeyList,
|
||||
}
|
||||
|
||||
impl From<PublicTransaction> for FfiPublicTransactionBody {
|
||||
fn from(value: PublicTransaction) -> Self {
|
||||
let PublicTransaction {
|
||||
hash,
|
||||
message,
|
||||
witness_set,
|
||||
} = value;
|
||||
|
||||
Self {
|
||||
hash: hash.into(),
|
||||
message: message.into(),
|
||||
witness_set: witness_set
|
||||
.signatures_and_public_keys
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.collect::<Vec<_>>()
|
||||
.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Box<FfiPublicTransactionBody>> for PublicTransaction {
|
||||
fn from(value: Box<FfiPublicTransactionBody>) -> Self {
|
||||
Self {
|
||||
hash: HashType(value.hash.data),
|
||||
message: PublicMessage {
|
||||
program_id: ProgramId(value.message.program_id.data),
|
||||
account_ids: {
|
||||
let std_vec: Vec<_> = value.message.account_ids.into();
|
||||
std_vec
|
||||
.into_iter()
|
||||
.map(|ffi_val| AccountId {
|
||||
value: ffi_val.data,
|
||||
})
|
||||
.collect()
|
||||
},
|
||||
nonces: {
|
||||
let std_vec: Vec<_> = value.message.nonces.into();
|
||||
std_vec.into_iter().map(Into::into).collect()
|
||||
},
|
||||
instruction_data: value.message.instruction_data.into(),
|
||||
},
|
||||
witness_set: WitnessSet {
|
||||
signatures_and_public_keys: {
|
||||
let std_vec: Vec<_> = value.witness_set.into();
|
||||
std_vec
|
||||
.into_iter()
|
||||
.map(|ffi_val| {
|
||||
(
|
||||
Signature(ffi_val.signature.data),
|
||||
PublicKey(ffi_val.public_key.data),
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
},
|
||||
proof: None,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct FfiPublicMessage {
|
||||
pub program_id: FfiProgramId,
|
||||
pub account_ids: FfiAccountIdList,
|
||||
pub nonces: FfiNonceList,
|
||||
pub instruction_data: FfiInstructionDataList,
|
||||
}
|
||||
|
||||
impl From<PublicMessage> for FfiPublicMessage {
|
||||
fn from(value: PublicMessage) -> Self {
|
||||
let PublicMessage {
|
||||
program_id,
|
||||
account_ids,
|
||||
nonces,
|
||||
instruction_data,
|
||||
} = value;
|
||||
|
||||
Self {
|
||||
program_id: program_id.into(),
|
||||
account_ids: account_ids
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.collect::<Vec<_>>()
|
||||
.into(),
|
||||
nonces: nonces
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.collect::<Vec<_>>()
|
||||
.into(),
|
||||
instruction_data: instruction_data.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct FfiPrivateTransactionBody {
|
||||
pub hash: FfiHashType,
|
||||
pub message: FfiPrivacyPreservingMessage,
|
||||
pub witness_set: FfiSignaturePubKeyList,
|
||||
pub proof: FfiProof,
|
||||
}
|
||||
|
||||
impl From<PrivacyPreservingTransaction> for FfiPrivateTransactionBody {
|
||||
fn from(value: PrivacyPreservingTransaction) -> Self {
|
||||
let PrivacyPreservingTransaction {
|
||||
hash,
|
||||
message,
|
||||
witness_set,
|
||||
} = value;
|
||||
|
||||
Self {
|
||||
hash: hash.into(),
|
||||
message: message.into(),
|
||||
witness_set: witness_set
|
||||
.signatures_and_public_keys
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.collect::<Vec<_>>()
|
||||
.into(),
|
||||
proof: witness_set
|
||||
.proof
|
||||
.expect("Private execution: proof must be present")
|
||||
.0
|
||||
.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Box<FfiPrivateTransactionBody>> for PrivacyPreservingTransaction {
|
||||
fn from(value: Box<FfiPrivateTransactionBody>) -> Self {
|
||||
Self {
|
||||
hash: HashType(value.hash.data),
|
||||
message: PrivacyPreservingMessage {
|
||||
public_account_ids: {
|
||||
let std_vec: Vec<_> = value.message.public_account_ids.into();
|
||||
std_vec
|
||||
.into_iter()
|
||||
.map(|ffi_val| AccountId {
|
||||
value: ffi_val.data,
|
||||
})
|
||||
.collect()
|
||||
},
|
||||
nonces: {
|
||||
let std_vec: Vec<_> = value.message.nonces.into();
|
||||
std_vec.into_iter().map(Into::into).collect()
|
||||
},
|
||||
public_post_states: {
|
||||
let std_vec: Vec<_> = value.message.public_post_states.into();
|
||||
std_vec.into_iter().map(Into::into).collect()
|
||||
},
|
||||
encrypted_private_post_states: {
|
||||
let std_vec: Vec<_> = value.message.encrypted_private_post_states.into();
|
||||
std_vec
|
||||
.into_iter()
|
||||
.map(|ffi_val| EncryptedAccountData {
|
||||
ciphertext: Ciphertext(ffi_val.ciphertext.into()),
|
||||
epk: EphemeralPublicKey(ffi_val.epk.into()),
|
||||
view_tag: ffi_val.view_tag,
|
||||
})
|
||||
.collect()
|
||||
},
|
||||
new_commitments: {
|
||||
let std_vec: Vec<_> = value.message.new_commitments.into();
|
||||
std_vec
|
||||
.into_iter()
|
||||
.map(|ffi_val| Commitment(ffi_val.data))
|
||||
.collect()
|
||||
},
|
||||
new_nullifiers: {
|
||||
let std_vec: Vec<_> = value.message.new_nullifiers.into();
|
||||
std_vec
|
||||
.into_iter()
|
||||
.map(|ffi_val| {
|
||||
(
|
||||
Nullifier(ffi_val.nullifier.data),
|
||||
CommitmentSetDigest(ffi_val.commitment_set_digest.data),
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
},
|
||||
block_validity_window: cast_ffi_validity_window(
|
||||
value.message.block_validity_window,
|
||||
),
|
||||
timestamp_validity_window: cast_ffi_validity_window(
|
||||
value.message.timestamp_validity_window,
|
||||
),
|
||||
},
|
||||
witness_set: WitnessSet {
|
||||
signatures_and_public_keys: {
|
||||
let std_vec: Vec<_> = value.witness_set.into();
|
||||
std_vec
|
||||
.into_iter()
|
||||
.map(|ffi_val| {
|
||||
(
|
||||
Signature(ffi_val.signature.data),
|
||||
PublicKey(ffi_val.public_key.data),
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
},
|
||||
proof: Some(Proof(value.proof.into())),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct FfiPrivacyPreservingMessage {
|
||||
pub public_account_ids: FfiAccountIdList,
|
||||
pub nonces: FfiNonceList,
|
||||
pub public_post_states: FfiAccountList,
|
||||
pub encrypted_private_post_states: FfiEncryptedAccountDataList,
|
||||
pub new_commitments: FfiVecBytes32,
|
||||
pub new_nullifiers: FfiNullifierCommitmentSetList,
|
||||
pub block_validity_window: [u64; 2],
|
||||
pub timestamp_validity_window: [u64; 2],
|
||||
}
|
||||
|
||||
impl From<PrivacyPreservingMessage> for FfiPrivacyPreservingMessage {
|
||||
fn from(value: PrivacyPreservingMessage) -> Self {
|
||||
let PrivacyPreservingMessage {
|
||||
public_account_ids,
|
||||
nonces,
|
||||
public_post_states,
|
||||
encrypted_private_post_states,
|
||||
new_commitments,
|
||||
new_nullifiers,
|
||||
block_validity_window,
|
||||
timestamp_validity_window,
|
||||
} = value;
|
||||
|
||||
Self {
|
||||
public_account_ids: public_account_ids
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.collect::<Vec<_>>()
|
||||
.into(),
|
||||
nonces: nonces
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.collect::<Vec<_>>()
|
||||
.into(),
|
||||
public_post_states: public_post_states
|
||||
.into_iter()
|
||||
.map(|acc_ind| -> nssa::Account {
|
||||
acc_ind.try_into().expect("Source is in blocks, must fit")
|
||||
})
|
||||
.map(Into::into)
|
||||
.collect::<Vec<_>>()
|
||||
.into(),
|
||||
encrypted_private_post_states: encrypted_private_post_states
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.collect::<Vec<_>>()
|
||||
.into(),
|
||||
new_commitments: new_commitments
|
||||
.into_iter()
|
||||
.map(|comm| FfiBytes32 { data: comm.0 })
|
||||
.collect::<Vec<_>>()
|
||||
.into(),
|
||||
new_nullifiers: new_nullifiers
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.collect::<Vec<_>>()
|
||||
.into(),
|
||||
block_validity_window: cast_validity_window(block_validity_window),
|
||||
timestamp_validity_window: cast_validity_window(timestamp_validity_window),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct FfiNullifierCommitmentSet {
|
||||
pub nullifier: FfiBytes32,
|
||||
pub commitment_set_digest: FfiBytes32,
|
||||
}
|
||||
|
||||
impl From<(Nullifier, CommitmentSetDigest)> for FfiNullifierCommitmentSet {
|
||||
fn from(value: (Nullifier, CommitmentSetDigest)) -> Self {
|
||||
Self {
|
||||
nullifier: FfiBytes32 { data: value.0.0 },
|
||||
commitment_set_digest: FfiBytes32 { data: value.1.0 },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct FfiEncryptedAccountData {
|
||||
pub ciphertext: FfiVecU8,
|
||||
pub epk: FfiVecU8,
|
||||
pub view_tag: u8,
|
||||
}
|
||||
|
||||
impl From<EncryptedAccountData> for FfiEncryptedAccountData {
|
||||
fn from(value: EncryptedAccountData) -> Self {
|
||||
let EncryptedAccountData {
|
||||
ciphertext,
|
||||
epk,
|
||||
view_tag,
|
||||
} = value;
|
||||
|
||||
Self {
|
||||
ciphertext: ciphertext.0.into(),
|
||||
epk: epk.0.into(),
|
||||
view_tag,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct FfiSignaturePubKeyEntry {
|
||||
pub signature: FfiSignature,
|
||||
pub public_key: FfiPublicKey,
|
||||
}
|
||||
|
||||
impl From<(Signature, PublicKey)> for FfiSignaturePubKeyEntry {
|
||||
fn from(value: (Signature, PublicKey)) -> Self {
|
||||
Self {
|
||||
signature: value.0.into(),
|
||||
public_key: value.1.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct FfiProgramDeploymentTransactionBody {
|
||||
pub hash: FfiHashType,
|
||||
pub message: FfiProgramDeploymentMessage,
|
||||
}
|
||||
|
||||
impl From<Box<FfiProgramDeploymentTransactionBody>> for ProgramDeploymentTransaction {
|
||||
fn from(value: Box<FfiProgramDeploymentTransactionBody>) -> Self {
|
||||
Self {
|
||||
hash: HashType(value.hash.data),
|
||||
message: ProgramDeploymentMessage {
|
||||
bytecode: value.message.into(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ProgramDeploymentTransaction> for FfiProgramDeploymentTransactionBody {
|
||||
fn from(value: ProgramDeploymentTransaction) -> Self {
|
||||
let ProgramDeploymentTransaction { hash, message } = value;
|
||||
|
||||
Self {
|
||||
hash: hash.into(),
|
||||
message: message.bytecode.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct FfiTransactionBody {
|
||||
pub public_body: *mut FfiPublicTransactionBody,
|
||||
pub private_body: *mut FfiPrivateTransactionBody,
|
||||
pub program_deployment_body: *mut FfiProgramDeploymentTransactionBody,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct FfiTransaction {
|
||||
pub body: FfiTransactionBody,
|
||||
pub kind: FfiTransactionKind,
|
||||
}
|
||||
|
||||
impl From<Transaction> for FfiTransaction {
|
||||
fn from(value: Transaction) -> Self {
|
||||
match value {
|
||||
Transaction::Public(pub_tx) => Self {
|
||||
body: FfiTransactionBody {
|
||||
public_body: Box::into_raw(Box::new(pub_tx.into())),
|
||||
private_body: std::ptr::null_mut(),
|
||||
program_deployment_body: std::ptr::null_mut(),
|
||||
},
|
||||
kind: FfiTransactionKind::Public,
|
||||
},
|
||||
Transaction::PrivacyPreserving(priv_tx) => Self {
|
||||
body: FfiTransactionBody {
|
||||
public_body: std::ptr::null_mut(),
|
||||
private_body: Box::into_raw(Box::new(priv_tx.into())),
|
||||
program_deployment_body: std::ptr::null_mut(),
|
||||
},
|
||||
kind: FfiTransactionKind::Private,
|
||||
},
|
||||
Transaction::ProgramDeployment(pr_dep_tx) => Self {
|
||||
body: FfiTransactionBody {
|
||||
public_body: std::ptr::null_mut(),
|
||||
private_body: std::ptr::null_mut(),
|
||||
program_deployment_body: Box::into_raw(Box::new(pr_dep_tx.into())),
|
||||
},
|
||||
kind: FfiTransactionKind::ProgramDeploy,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub enum FfiTransactionKind {
|
||||
Public = 0x0,
|
||||
Private,
|
||||
ProgramDeploy,
|
||||
}
|
||||
|
||||
/// Frees the resources associated with the given ffi transaction.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// - `val`: An instance of `FfiTransaction`.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// void.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure that:
|
||||
/// - `val` is a valid instance of `FfiTransaction`.
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn free_ffi_transaction(val: FfiTransaction) {
|
||||
match val.kind {
|
||||
FfiTransactionKind::Public => {
|
||||
let body = unsafe { Box::from_raw(val.body.public_body) };
|
||||
let std_body: PublicTransaction = body.into();
|
||||
drop(std_body);
|
||||
}
|
||||
FfiTransactionKind::Private => {
|
||||
let body = unsafe { Box::from_raw(val.body.private_body) };
|
||||
let std_body: PrivacyPreservingTransaction = body.into();
|
||||
drop(std_body);
|
||||
}
|
||||
FfiTransactionKind::ProgramDeploy => {
|
||||
let body = unsafe { Box::from_raw(val.body.program_deployment_body) };
|
||||
let std_body: ProgramDeploymentTransaction = body.into();
|
||||
drop(std_body);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Frees the resources associated with the given ffi transaction option.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// - `val`: An instance of `FfiOption<FfiTransaction>`.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// void.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure that:
|
||||
/// - `val` is a valid instance of `FfiOption<FfiTransaction>`.
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn free_ffi_transaction_opt(val: FfiOption<FfiTransaction>) {
|
||||
if val.is_some {
|
||||
let value = unsafe { Box::from_raw(val.value) };
|
||||
|
||||
match value.kind {
|
||||
FfiTransactionKind::Public => {
|
||||
let body = unsafe { Box::from_raw(value.body.public_body) };
|
||||
let std_body: PublicTransaction = body.into();
|
||||
drop(std_body);
|
||||
}
|
||||
FfiTransactionKind::Private => {
|
||||
let body = unsafe { Box::from_raw(value.body.private_body) };
|
||||
let std_body: PrivacyPreservingTransaction = body.into();
|
||||
drop(std_body);
|
||||
}
|
||||
FfiTransactionKind::ProgramDeploy => {
|
||||
let body = unsafe { Box::from_raw(value.body.program_deployment_body) };
|
||||
let std_body: ProgramDeploymentTransaction = body.into();
|
||||
drop(std_body);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Frees the resources associated with the given vector of ffi transactions.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// - `val`: An instance of `FfiVec<FfiTransaction>`.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// void.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure that:
|
||||
/// - `val` is a valid instance of `FfiVec<FfiTransaction>`.
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn free_ffi_transaction_vec(val: FfiVec<FfiTransaction>) {
|
||||
let ffi_tx_std_vec: Vec<_> = val.into();
|
||||
for tx in ffi_tx_std_vec {
|
||||
unsafe {
|
||||
free_ffi_transaction(tx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn cast_validity_window(window: ValidityWindow) -> [u64; 2] {
|
||||
[
|
||||
window.0.0.unwrap_or_default(),
|
||||
window.0.1.unwrap_or(u64::MAX),
|
||||
]
|
||||
}
|
||||
|
||||
const fn cast_ffi_validity_window(ffi_window: [u64; 2]) -> ValidityWindow {
|
||||
let left = if ffi_window[0] == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(ffi_window[0])
|
||||
};
|
||||
|
||||
let right = if ffi_window[1] == u64::MAX {
|
||||
None
|
||||
} else {
|
||||
Some(ffi_window[1])
|
||||
};
|
||||
|
||||
ValidityWindow((left, right))
|
||||
}
|
||||
31
indexer/ffi/src/api/types/vectors.rs
Normal file
31
indexer/ffi/src/api/types/vectors.rs
Normal file
@ -0,0 +1,31 @@
|
||||
use crate::api::types::{
|
||||
FfiAccountId, FfiBytes32, FfiNonce, FfiVec,
|
||||
account::FfiAccount,
|
||||
transaction::{
|
||||
FfiEncryptedAccountData, FfiNullifierCommitmentSet, FfiSignaturePubKeyEntry, FfiTransaction,
|
||||
},
|
||||
};
|
||||
|
||||
pub type FfiVecU8 = FfiVec<u8>;
|
||||
|
||||
pub type FfiAccountList = FfiVec<FfiAccount>;
|
||||
|
||||
pub type FfiAccountIdList = FfiVec<FfiAccountId>;
|
||||
|
||||
pub type FfiVecBytes32 = FfiVec<FfiBytes32>;
|
||||
|
||||
pub type FfiBlockBody = FfiVec<FfiTransaction>;
|
||||
|
||||
pub type FfiNonceList = FfiVec<FfiNonce>;
|
||||
|
||||
pub type FfiInstructionDataList = FfiVec<u32>;
|
||||
|
||||
pub type FfiSignaturePubKeyList = FfiVec<FfiSignaturePubKeyEntry>;
|
||||
|
||||
pub type FfiProof = FfiVecU8;
|
||||
|
||||
pub type FfiProgramDeploymentMessage = FfiVecU8;
|
||||
|
||||
pub type FfiEncryptedAccountDataList = FfiVec<FfiEncryptedAccountData>;
|
||||
|
||||
pub type FfiNullifierCommitmentSetList = FfiVec<FfiNullifierCommitmentSet>;
|
||||
33
indexer/ffi/src/client.rs
Normal file
33
indexer/ffi/src/client.rs
Normal file
@ -0,0 +1,33 @@
|
||||
use std::{ops::Deref, sync::Arc};
|
||||
|
||||
use anyhow::{Context as _, Result};
|
||||
use log::info;
|
||||
pub use url::Url;
|
||||
|
||||
pub trait IndexerClientTrait: Clone {
|
||||
async fn new(indexer_url: &Url) -> Result<Self>;
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct IndexerClient(Arc<jsonrpsee::ws_client::WsClient>);
|
||||
|
||||
impl IndexerClientTrait for IndexerClient {
|
||||
async fn new(indexer_url: &Url) -> Result<Self> {
|
||||
info!("Connecting to Indexer at {indexer_url}");
|
||||
|
||||
let client = jsonrpsee::ws_client::WsClientBuilder::default()
|
||||
.build(indexer_url)
|
||||
.await
|
||||
.context("Failed to create websocket client")?;
|
||||
|
||||
Ok(Self(Arc::new(client)))
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for IndexerClient {
|
||||
type Target = jsonrpsee::ws_client::WsClient;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
@ -5,6 +5,7 @@ pub enum OperationStatus {
|
||||
Ok = 0x0,
|
||||
NullPointer = 0x1,
|
||||
InitializationError = 0x2,
|
||||
ClientError = 0x3,
|
||||
}
|
||||
|
||||
impl OperationStatus {
|
||||
@ -3,18 +3,26 @@ use std::{ffi::c_void, net::SocketAddr};
|
||||
use indexer_service::IndexerHandle;
|
||||
use tokio::runtime::Runtime;
|
||||
|
||||
use crate::client::IndexerClient;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct IndexerServiceFFI {
|
||||
indexer_handle: *mut c_void,
|
||||
runtime: *mut c_void,
|
||||
indexer_client: *mut c_void,
|
||||
}
|
||||
|
||||
impl IndexerServiceFFI {
|
||||
pub fn new(indexer_handle: indexer_service::IndexerHandle, runtime: Runtime) -> Self {
|
||||
pub fn new(
|
||||
indexer_handle: indexer_service::IndexerHandle,
|
||||
runtime: Runtime,
|
||||
indexer_client: IndexerClient,
|
||||
) -> Self {
|
||||
Self {
|
||||
// Box the complex types and convert to opaque pointers
|
||||
indexer_handle: Box::into_raw(Box::new(indexer_handle)).cast::<c_void>(),
|
||||
runtime: Box::into_raw(Box::new(runtime)).cast::<c_void>(),
|
||||
indexer_client: Box::into_raw(Box::new(indexer_client)).cast::<c_void>(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -25,10 +33,11 @@ impl IndexerServiceFFI {
|
||||
/// The caller must ensure that:
|
||||
/// - `self` is a valid object(contains valid pointers in all fields)
|
||||
#[must_use]
|
||||
pub unsafe fn into_parts(self) -> (Box<IndexerHandle>, Box<Runtime>) {
|
||||
pub unsafe fn into_parts(self) -> (Box<IndexerHandle>, Box<Runtime>, Box<IndexerClient>) {
|
||||
let indexer_handle = unsafe { Box::from_raw(self.indexer_handle.cast::<IndexerHandle>()) };
|
||||
let runtime = unsafe { Box::from_raw(self.runtime.cast::<Runtime>()) };
|
||||
(indexer_handle, runtime)
|
||||
let indexer_client = unsafe { Box::from_raw(self.indexer_client.cast::<IndexerClient>()) };
|
||||
(indexer_handle, runtime, indexer_client)
|
||||
}
|
||||
|
||||
/// Helper to get indexer handle addr.
|
||||
@ -49,7 +58,7 @@ impl IndexerServiceFFI {
|
||||
indexer_handle.addr()
|
||||
}
|
||||
|
||||
/// Helper to get indexer handle addr.
|
||||
/// Helper to get indexer handle ref.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
@ -64,6 +73,38 @@ impl IndexerServiceFFI {
|
||||
.expect("Indexer Handle must be non-null pointer")
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper to get indexer client ref.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure that:
|
||||
/// - `self` is a valid object(contains valid pointers in all fields)
|
||||
#[must_use]
|
||||
pub const unsafe fn client(&self) -> &IndexerClient {
|
||||
unsafe {
|
||||
self.indexer_client
|
||||
.cast::<IndexerClient>()
|
||||
.as_ref()
|
||||
.expect("Indexer Client must be non-null pointer")
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper to get indexer runtime ref.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure that:
|
||||
/// - `self` is a valid object(contains valid pointers in all fields)
|
||||
#[must_use]
|
||||
pub const unsafe fn runtime(&self) -> &Runtime {
|
||||
unsafe {
|
||||
self.runtime
|
||||
.cast::<Runtime>()
|
||||
.as_ref()
|
||||
.expect("Indexer Runtime must be non-null pointer")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Implement Drop to prevent memory leaks
|
||||
@ -72,6 +113,7 @@ impl Drop for IndexerServiceFFI {
|
||||
let Self {
|
||||
indexer_handle,
|
||||
runtime,
|
||||
indexer_client,
|
||||
} = self;
|
||||
|
||||
if indexer_handle.is_null() {
|
||||
@ -80,7 +122,11 @@ impl Drop for IndexerServiceFFI {
|
||||
if runtime.is_null() {
|
||||
log::error!("Attempted to drop a null tokio runtime pointer. This is a bug");
|
||||
}
|
||||
if indexer_client.is_null() {
|
||||
log::error!("Attempted to drop a null client pointer. This is a bug");
|
||||
}
|
||||
drop(unsafe { Box::from_raw(indexer_handle.cast::<IndexerHandle>()) });
|
||||
drop(unsafe { Box::from_raw(runtime.cast::<Runtime>()) });
|
||||
drop(unsafe { Box::from_raw(indexer_client.cast::<IndexerClient>()) });
|
||||
}
|
||||
}
|
||||
@ -4,5 +4,6 @@ pub use errors::OperationStatus;
|
||||
pub use indexer::IndexerServiceFFI;
|
||||
|
||||
pub mod api;
|
||||
mod client;
|
||||
mod errors;
|
||||
mod indexer;
|
||||
@ -1,76 +0,0 @@
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef enum OperationStatus {
|
||||
Ok = 0,
|
||||
NullPointer = 1,
|
||||
InitializationError = 2,
|
||||
} OperationStatus;
|
||||
|
||||
typedef struct IndexerServiceFFI {
|
||||
void *indexer_handle;
|
||||
void *runtime;
|
||||
} IndexerServiceFFI;
|
||||
|
||||
/**
|
||||
* Simple wrapper around a pointer to a value or an error.
|
||||
*
|
||||
* Pointer is not guaranteed. You should check the error field before
|
||||
* dereferencing the pointer.
|
||||
*/
|
||||
typedef struct PointerResult_IndexerServiceFFI__OperationStatus {
|
||||
struct IndexerServiceFFI *value;
|
||||
enum OperationStatus error;
|
||||
} PointerResult_IndexerServiceFFI__OperationStatus;
|
||||
|
||||
typedef struct PointerResult_IndexerServiceFFI__OperationStatus InitializedIndexerServiceFFIResult;
|
||||
|
||||
/**
|
||||
* Creates and starts an indexer based on the provided
|
||||
* configuration file path.
|
||||
*
|
||||
* # Arguments
|
||||
*
|
||||
* - `config_path`: A pointer to a string representing the path to the configuration file.
|
||||
* - `port`: Number representing a port, on which indexers RPC will start.
|
||||
*
|
||||
* # Returns
|
||||
*
|
||||
* An `InitializedIndexerServiceFFIResult` containing either a pointer to the
|
||||
* initialized `IndexerServiceFFI` or an error code.
|
||||
*/
|
||||
InitializedIndexerServiceFFIResult start_indexer(const char *config_path, uint16_t port);
|
||||
|
||||
/**
|
||||
* Stops and frees the resources associated with the given indexer service.
|
||||
*
|
||||
* # Arguments
|
||||
*
|
||||
* - `indexer`: A pointer to the `IndexerServiceFFI` instance to be stopped.
|
||||
*
|
||||
* # Returns
|
||||
*
|
||||
* An `OperationStatus` indicating success or failure.
|
||||
*
|
||||
* # Safety
|
||||
*
|
||||
* The caller must ensure that:
|
||||
* - `indexer` is a valid pointer to a `IndexerServiceFFI` instance
|
||||
* - The `IndexerServiceFFI` instance was created by this library
|
||||
* - The pointer will not be used after this function returns
|
||||
*/
|
||||
enum OperationStatus stop_indexer(struct IndexerServiceFFI *indexer);
|
||||
|
||||
/**
|
||||
* # Safety
|
||||
* It's up to the caller to pass a proper pointer, if somehow from c/c++ side
|
||||
* this is called with a type which doesn't come from a returned `CString` it
|
||||
* will cause a segfault.
|
||||
*/
|
||||
void free_cstring(char *block);
|
||||
|
||||
bool is_ok(const enum OperationStatus *self);
|
||||
|
||||
bool is_error(const enum OperationStatus *self);
|
||||
@ -25,6 +25,7 @@ jsonrpsee = { workspace = true, features = ["ws-client"] }
|
||||
wallet-ffi.workspace = true
|
||||
indexer_ffi.workspace = true
|
||||
testnet_initial_state.workspace = true
|
||||
indexer_service_protocol.workspace = true
|
||||
|
||||
url.workspace = true
|
||||
|
||||
|
||||
@ -266,6 +266,11 @@ impl BlockingTestContextFFI {
|
||||
pub fn runtime_clone(&self) -> Arc<tokio::runtime::Runtime> {
|
||||
Arc::<tokio::runtime::Runtime>::clone(&self.runtime)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn indexer_ffi(&self) -> *const IndexerServiceFFI {
|
||||
&raw const (self.indexer_ffi)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for BlockingTestContextFFI {
|
||||
|
||||
@ -1,11 +1,18 @@
|
||||
#![expect(
|
||||
clippy::shadow_unrelated,
|
||||
clippy::tests_outside_test_module,
|
||||
clippy::undocumented_unsafe_blocks,
|
||||
reason = "We don't care about these in tests"
|
||||
)]
|
||||
|
||||
use anyhow::{Context as _, Result};
|
||||
use indexer_service_rpc::RpcClient as _;
|
||||
use indexer_ffi::{
|
||||
IndexerServiceFFI, OperationStatus,
|
||||
api::{
|
||||
PointerResult,
|
||||
types::{FfiAccountId, FfiOption, FfiVec, account::FfiAccount, block::FfiBlock},
|
||||
},
|
||||
};
|
||||
use integration_tests::{
|
||||
TIME_TO_WAIT_FOR_BLOCK_SECONDS, format_private_account_id, format_public_account_id,
|
||||
test_context_ffi::BlockingTestContextFFI, verify_commitment_is_in_state,
|
||||
@ -17,6 +24,23 @@ use wallet::cli::{Command, programs::native_token_transfer::AuthTransferSubcomma
|
||||
/// Maximum time to wait for the indexer to catch up to the sequencer.
|
||||
const L2_TO_L1_TIMEOUT_MILLIS: u64 = 180_000;
|
||||
|
||||
unsafe extern "C" {
|
||||
unsafe fn query_last_block(
|
||||
indexer: *const IndexerServiceFFI,
|
||||
) -> PointerResult<u64, OperationStatus>;
|
||||
|
||||
unsafe fn query_block_vec(
|
||||
indexer: *const IndexerServiceFFI,
|
||||
before: FfiOption<u64>,
|
||||
limit: u64,
|
||||
) -> PointerResult<FfiVec<FfiBlock>, OperationStatus>;
|
||||
|
||||
unsafe fn query_account(
|
||||
indexer: *const IndexerServiceFFI,
|
||||
account_id: FfiAccountId,
|
||||
) -> PointerResult<FfiAccount, OperationStatus>;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn indexer_test_run_ffi() -> Result<()> {
|
||||
let blocking_ctx = BlockingTestContextFFI::new()?;
|
||||
@ -28,10 +52,19 @@ fn indexer_test_run_ffi() -> Result<()> {
|
||||
});
|
||||
|
||||
let last_block_indexer = blocking_ctx.ctx().get_last_block_indexer(runtime_wrapped)?;
|
||||
let last_block_indexer_ffi_res = unsafe { query_last_block(blocking_ctx.indexer_ffi()) };
|
||||
|
||||
assert!(last_block_indexer_ffi_res.error.is_ok());
|
||||
|
||||
let last_block_indexer_ffi = unsafe { *last_block_indexer_ffi_res.value };
|
||||
|
||||
info!("Last block on ind now is {last_block_indexer}");
|
||||
info!("Last block on ind ffi now is {last_block_indexer_ffi}");
|
||||
|
||||
assert!(last_block_indexer > 1);
|
||||
assert!(last_block_indexer_ffi > 1);
|
||||
|
||||
assert_eq!(last_block_indexer, last_block_indexer_ffi);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -40,7 +73,6 @@ fn indexer_test_run_ffi() -> Result<()> {
|
||||
fn indexer_ffi_block_batching() -> Result<()> {
|
||||
let blocking_ctx = BlockingTestContextFFI::new()?;
|
||||
let runtime_wrapped = blocking_ctx.runtime();
|
||||
let ctx = blocking_ctx.ctx();
|
||||
|
||||
// WAIT
|
||||
info!("Waiting for indexer to parse blocks");
|
||||
@ -48,31 +80,36 @@ fn indexer_ffi_block_batching() -> Result<()> {
|
||||
tokio::time::sleep(std::time::Duration::from_millis(L2_TO_L1_TIMEOUT_MILLIS)).await;
|
||||
});
|
||||
|
||||
let last_block_indexer = runtime_wrapped
|
||||
.block_on(ctx.indexer_client().get_last_finalized_block_id())
|
||||
.unwrap();
|
||||
let last_block_indexer_ffi_res = unsafe { query_last_block(blocking_ctx.indexer_ffi()) };
|
||||
|
||||
assert!(last_block_indexer_ffi_res.error.is_ok());
|
||||
|
||||
let last_block_indexer = unsafe { *last_block_indexer_ffi_res.value };
|
||||
|
||||
info!("Last block on ind now is {last_block_indexer}");
|
||||
|
||||
assert!(last_block_indexer > 1);
|
||||
|
||||
// Getting wide batch to fit all blocks (from latest backwards)
|
||||
let mut block_batch = runtime_wrapped
|
||||
.block_on(ctx.indexer_client().get_blocks(None, 100))
|
||||
.unwrap();
|
||||
let before_ffi = FfiOption::<u64>::from_none();
|
||||
let limit = 100;
|
||||
|
||||
// Reverse to check chain consistency from oldest to newest
|
||||
block_batch.reverse();
|
||||
let block_batch_ffi_res =
|
||||
unsafe { query_block_vec(blocking_ctx.indexer_ffi(), before_ffi, limit) };
|
||||
|
||||
// Checking chain consistency
|
||||
let mut prev_block_hash = block_batch.first().unwrap().header.hash;
|
||||
assert!(block_batch_ffi_res.error.is_ok());
|
||||
|
||||
for block in &block_batch[1..] {
|
||||
assert_eq!(block.header.prev_block_hash, prev_block_hash);
|
||||
let block_batch = unsafe { &*block_batch_ffi_res.value };
|
||||
|
||||
let mut last_block_prev_hash = unsafe { block_batch.get(0) }.header.prev_block_hash.data;
|
||||
|
||||
for i in 1..block_batch.len {
|
||||
let block = unsafe { block_batch.get(i) };
|
||||
|
||||
assert_eq!(last_block_prev_hash, block.header.hash.data);
|
||||
|
||||
info!("Block {} chain-consistent", block.header.block_id);
|
||||
|
||||
prev_block_hash = block.header.hash;
|
||||
last_block_prev_hash = block.header.prev_block_hash.data;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -82,6 +119,7 @@ fn indexer_ffi_block_batching() -> Result<()> {
|
||||
fn indexer_ffi_state_consistency() -> Result<()> {
|
||||
let mut blocking_ctx = BlockingTestContextFFI::new()?;
|
||||
let runtime_wrapped = blocking_ctx.runtime_clone();
|
||||
let indexer_ffi = blocking_ctx.indexer_ffi();
|
||||
let ctx = blocking_ctx.ctx_mut();
|
||||
|
||||
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
|
||||
@ -175,14 +213,21 @@ fn indexer_ffi_state_consistency() -> Result<()> {
|
||||
tokio::time::sleep(std::time::Duration::from_millis(L2_TO_L1_TIMEOUT_MILLIS)).await;
|
||||
});
|
||||
|
||||
let acc1_ind_state = runtime_wrapped.block_on(
|
||||
ctx.indexer_client()
|
||||
.get_account(ctx.existing_public_accounts()[0].into()),
|
||||
)?;
|
||||
let acc2_ind_state = runtime_wrapped.block_on(
|
||||
ctx.indexer_client()
|
||||
.get_account(ctx.existing_public_accounts()[1].into()),
|
||||
)?;
|
||||
let acc1_ind_state_ffi =
|
||||
unsafe { query_account(indexer_ffi, (&ctx.existing_public_accounts()[0]).into()) };
|
||||
|
||||
assert!(acc1_ind_state_ffi.error.is_ok());
|
||||
|
||||
let acc1_ind_state_pre = unsafe { &*acc1_ind_state_ffi.value };
|
||||
let acc1_ind_state: indexer_service_protocol::Account = acc1_ind_state_pre.into();
|
||||
|
||||
let acc2_ind_state_ffi =
|
||||
unsafe { query_account(indexer_ffi, (&ctx.existing_public_accounts()[1]).into()) };
|
||||
|
||||
assert!(acc2_ind_state_ffi.error.is_ok());
|
||||
|
||||
let acc2_ind_state_pre = unsafe { &*acc2_ind_state_ffi.value };
|
||||
let acc2_ind_state: indexer_service_protocol::Account = acc2_ind_state_pre.into();
|
||||
|
||||
info!("Checking correct state transition");
|
||||
let acc1_seq_state =
|
||||
@ -208,6 +253,7 @@ fn indexer_ffi_state_consistency() -> Result<()> {
|
||||
fn indexer_ffi_state_consistency_with_labels() -> Result<()> {
|
||||
let mut blocking_ctx = BlockingTestContextFFI::new()?;
|
||||
let runtime_wrapped = blocking_ctx.runtime_clone();
|
||||
let indexer_ffi = blocking_ctx.indexer_ffi();
|
||||
let ctx = blocking_ctx.ctx_mut();
|
||||
|
||||
// Assign labels to both accounts
|
||||
@ -269,10 +315,14 @@ fn indexer_ffi_state_consistency_with_labels() -> Result<()> {
|
||||
tokio::time::sleep(std::time::Duration::from_millis(L2_TO_L1_TIMEOUT_MILLIS)).await;
|
||||
});
|
||||
|
||||
let acc1_ind_state = runtime_wrapped.block_on(
|
||||
ctx.indexer_client()
|
||||
.get_account(ctx.existing_public_accounts()[0].into()),
|
||||
)?;
|
||||
let acc1_ind_state_ffi =
|
||||
unsafe { query_account(indexer_ffi, (&ctx.existing_public_accounts()[0]).into()) };
|
||||
|
||||
assert!(acc1_ind_state_ffi.error.is_ok());
|
||||
|
||||
let acc1_ind_state_pre = unsafe { &*acc1_ind_state_ffi.value };
|
||||
let acc1_ind_state: indexer_service_protocol::Account = acc1_ind_state_pre.into();
|
||||
|
||||
let acc1_seq_state =
|
||||
runtime_wrapped.block_on(sequencer_service_rpc::RpcClient::get_account(
|
||||
ctx.sequencer_client(),
|
||||
|
||||
@ -15,6 +15,7 @@ wallet.workspace = true
|
||||
nssa.workspace = true
|
||||
nssa_core.workspace = true
|
||||
sequencer_service_rpc = { workspace = true, features = ["client"] }
|
||||
|
||||
tokio.workspace = true
|
||||
|
||||
[build-dependencies]
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user