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-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-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-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"
|
yanked = "deny"
|
||||||
unused-ignored-advisory = "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"
|
checksum = "7ab67060fc6b8ef687992d439ca0fa36e7ed17e9a0b16b25b601e8757df720de"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"data-encoding",
|
"data-encoding",
|
||||||
"syn 2.0.117",
|
"syn 1.0.109",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2558,7 +2558,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -3797,10 +3797,16 @@ dependencies = [
|
|||||||
name = "indexer_ffi"
|
name = "indexer_ffi"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
"cbindgen",
|
"cbindgen",
|
||||||
"indexer_service",
|
"indexer_service",
|
||||||
|
"indexer_service_protocol",
|
||||||
|
"indexer_service_rpc",
|
||||||
|
"jsonrpsee",
|
||||||
"log",
|
"log",
|
||||||
|
"nssa",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"url",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -3916,6 +3922,7 @@ dependencies = [
|
|||||||
"hex",
|
"hex",
|
||||||
"indexer_ffi",
|
"indexer_ffi",
|
||||||
"indexer_service",
|
"indexer_service",
|
||||||
|
"indexer_service_protocol",
|
||||||
"indexer_service_rpc",
|
"indexer_service_rpc",
|
||||||
"jsonrpsee",
|
"jsonrpsee",
|
||||||
"key_protocol",
|
"key_protocol",
|
||||||
@ -6335,7 +6342,7 @@ version = "0.50.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
|
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-sys 0.61.2",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -8120,7 +8127,7 @@ dependencies = [
|
|||||||
"errno",
|
"errno",
|
||||||
"libc",
|
"libc",
|
||||||
"linux-raw-sys",
|
"linux-raw-sys",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -9089,7 +9096,7 @@ dependencies = [
|
|||||||
"getrandom 0.4.2",
|
"getrandom 0.4.2",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"rustix",
|
"rustix",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -10375,7 +10382,7 @@ version = "0.1.11"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
|
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|||||||
@ -37,7 +37,7 @@ members = [
|
|||||||
"examples/program_deployment/methods",
|
"examples/program_deployment/methods",
|
||||||
"examples/program_deployment/methods/guest",
|
"examples/program_deployment/methods/guest",
|
||||||
"testnet_initial_state",
|
"testnet_initial_state",
|
||||||
"indexer_ffi",
|
"indexer/ffi",
|
||||||
]
|
]
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
@ -57,7 +57,7 @@ indexer_service_protocol = { path = "indexer/service/protocol" }
|
|||||||
indexer_service_rpc = { path = "indexer/service/rpc" }
|
indexer_service_rpc = { path = "indexer/service/rpc" }
|
||||||
wallet = { path = "wallet" }
|
wallet = { path = "wallet" }
|
||||||
wallet-ffi = { path = "wallet-ffi", default-features = false }
|
wallet-ffi = { path = "wallet-ffi", default-features = false }
|
||||||
indexer_ffi = { path = "indexer_ffi" }
|
indexer_ffi = { path = "indexer/ffi" }
|
||||||
clock_core = { path = "programs/clock/core" }
|
clock_core = { path = "programs/clock/core" }
|
||||||
token_core = { path = "programs/token/core" }
|
token_core = { path = "programs/token/core" }
|
||||||
token_program = { path = "programs/token" }
|
token_program = { path = "programs/token" }
|
||||||
|
|||||||
@ -5,9 +5,16 @@ name = "indexer_ffi"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
nssa.workspace = true
|
||||||
indexer_service.workspace = true
|
indexer_service.workspace = true
|
||||||
|
indexer_service_rpc = { workspace = true, features = ["client"] }
|
||||||
|
indexer_service_protocol.workspace = true
|
||||||
|
|
||||||
|
url.workspace = true
|
||||||
log = { workspace = true }
|
log = { workspace = true }
|
||||||
tokio = { features = ["rt-multi-thread"], workspace = true }
|
tokio = { features = ["rt-multi-thread"], workspace = true }
|
||||||
|
jsonrpsee.workspace = true
|
||||||
|
anyhow.workspace = true
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
cbindgen = "0.29"
|
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 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>;
|
pub type InitializedIndexerServiceFFIResult = PointerResult<IndexerServiceFFI, OperationStatus>;
|
||||||
|
|
||||||
@ -67,7 +75,13 @@ fn setup_indexer(
|
|||||||
OperationStatus::InitializationError
|
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.
|
/// Stops and frees the resources associated with the given indexer service.
|
||||||
@ -1,5 +1,8 @@
|
|||||||
pub use result::PointerResult;
|
pub use result::PointerResult;
|
||||||
|
|
||||||
|
pub mod client;
|
||||||
pub mod lifecycle;
|
pub mod lifecycle;
|
||||||
pub mod memory;
|
pub mod memory;
|
||||||
|
pub mod query;
|
||||||
pub mod result;
|
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,
|
Ok = 0x0,
|
||||||
NullPointer = 0x1,
|
NullPointer = 0x1,
|
||||||
InitializationError = 0x2,
|
InitializationError = 0x2,
|
||||||
|
ClientError = 0x3,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OperationStatus {
|
impl OperationStatus {
|
||||||
@ -3,18 +3,26 @@ use std::{ffi::c_void, net::SocketAddr};
|
|||||||
use indexer_service::IndexerHandle;
|
use indexer_service::IndexerHandle;
|
||||||
use tokio::runtime::Runtime;
|
use tokio::runtime::Runtime;
|
||||||
|
|
||||||
|
use crate::client::IndexerClient;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct IndexerServiceFFI {
|
pub struct IndexerServiceFFI {
|
||||||
indexer_handle: *mut c_void,
|
indexer_handle: *mut c_void,
|
||||||
runtime: *mut c_void,
|
runtime: *mut c_void,
|
||||||
|
indexer_client: *mut c_void,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IndexerServiceFFI {
|
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 {
|
Self {
|
||||||
// Box the complex types and convert to opaque pointers
|
// Box the complex types and convert to opaque pointers
|
||||||
indexer_handle: Box::into_raw(Box::new(indexer_handle)).cast::<c_void>(),
|
indexer_handle: Box::into_raw(Box::new(indexer_handle)).cast::<c_void>(),
|
||||||
runtime: Box::into_raw(Box::new(runtime)).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:
|
/// The caller must ensure that:
|
||||||
/// - `self` is a valid object(contains valid pointers in all fields)
|
/// - `self` is a valid object(contains valid pointers in all fields)
|
||||||
#[must_use]
|
#[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 indexer_handle = unsafe { Box::from_raw(self.indexer_handle.cast::<IndexerHandle>()) };
|
||||||
let runtime = unsafe { Box::from_raw(self.runtime.cast::<Runtime>()) };
|
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.
|
/// Helper to get indexer handle addr.
|
||||||
@ -49,7 +58,7 @@ impl IndexerServiceFFI {
|
|||||||
indexer_handle.addr()
|
indexer_handle.addr()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Helper to get indexer handle addr.
|
/// Helper to get indexer handle ref.
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
@ -64,6 +73,38 @@ impl IndexerServiceFFI {
|
|||||||
.expect("Indexer Handle must be non-null pointer")
|
.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
|
// Implement Drop to prevent memory leaks
|
||||||
@ -72,6 +113,7 @@ impl Drop for IndexerServiceFFI {
|
|||||||
let Self {
|
let Self {
|
||||||
indexer_handle,
|
indexer_handle,
|
||||||
runtime,
|
runtime,
|
||||||
|
indexer_client,
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
if indexer_handle.is_null() {
|
if indexer_handle.is_null() {
|
||||||
@ -80,7 +122,11 @@ impl Drop for IndexerServiceFFI {
|
|||||||
if runtime.is_null() {
|
if runtime.is_null() {
|
||||||
log::error!("Attempted to drop a null tokio runtime pointer. This is a bug");
|
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(indexer_handle.cast::<IndexerHandle>()) });
|
||||||
drop(unsafe { Box::from_raw(runtime.cast::<Runtime>()) });
|
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 use indexer::IndexerServiceFFI;
|
||||||
|
|
||||||
pub mod api;
|
pub mod api;
|
||||||
|
mod client;
|
||||||
mod errors;
|
mod errors;
|
||||||
mod indexer;
|
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
|
wallet-ffi.workspace = true
|
||||||
indexer_ffi.workspace = true
|
indexer_ffi.workspace = true
|
||||||
testnet_initial_state.workspace = true
|
testnet_initial_state.workspace = true
|
||||||
|
indexer_service_protocol.workspace = true
|
||||||
|
|
||||||
url.workspace = true
|
url.workspace = true
|
||||||
|
|
||||||
|
|||||||
@ -266,6 +266,11 @@ impl BlockingTestContextFFI {
|
|||||||
pub fn runtime_clone(&self) -> Arc<tokio::runtime::Runtime> {
|
pub fn runtime_clone(&self) -> Arc<tokio::runtime::Runtime> {
|
||||||
Arc::<tokio::runtime::Runtime>::clone(&self.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 {
|
impl Drop for BlockingTestContextFFI {
|
||||||
|
|||||||
@ -1,11 +1,18 @@
|
|||||||
#![expect(
|
#![expect(
|
||||||
clippy::shadow_unrelated,
|
clippy::shadow_unrelated,
|
||||||
clippy::tests_outside_test_module,
|
clippy::tests_outside_test_module,
|
||||||
|
clippy::undocumented_unsafe_blocks,
|
||||||
reason = "We don't care about these in tests"
|
reason = "We don't care about these in tests"
|
||||||
)]
|
)]
|
||||||
|
|
||||||
use anyhow::{Context as _, Result};
|
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::{
|
use integration_tests::{
|
||||||
TIME_TO_WAIT_FOR_BLOCK_SECONDS, format_private_account_id, format_public_account_id,
|
TIME_TO_WAIT_FOR_BLOCK_SECONDS, format_private_account_id, format_public_account_id,
|
||||||
test_context_ffi::BlockingTestContextFFI, verify_commitment_is_in_state,
|
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.
|
/// Maximum time to wait for the indexer to catch up to the sequencer.
|
||||||
const L2_TO_L1_TIMEOUT_MILLIS: u64 = 180_000;
|
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]
|
#[test]
|
||||||
fn indexer_test_run_ffi() -> Result<()> {
|
fn indexer_test_run_ffi() -> Result<()> {
|
||||||
let blocking_ctx = BlockingTestContextFFI::new()?;
|
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 = 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 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 > 1);
|
||||||
|
assert!(last_block_indexer_ffi > 1);
|
||||||
|
|
||||||
|
assert_eq!(last_block_indexer, last_block_indexer_ffi);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -40,7 +73,6 @@ fn indexer_test_run_ffi() -> Result<()> {
|
|||||||
fn indexer_ffi_block_batching() -> Result<()> {
|
fn indexer_ffi_block_batching() -> Result<()> {
|
||||||
let blocking_ctx = BlockingTestContextFFI::new()?;
|
let blocking_ctx = BlockingTestContextFFI::new()?;
|
||||||
let runtime_wrapped = blocking_ctx.runtime();
|
let runtime_wrapped = blocking_ctx.runtime();
|
||||||
let ctx = blocking_ctx.ctx();
|
|
||||||
|
|
||||||
// WAIT
|
// WAIT
|
||||||
info!("Waiting for indexer to parse blocks");
|
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;
|
tokio::time::sleep(std::time::Duration::from_millis(L2_TO_L1_TIMEOUT_MILLIS)).await;
|
||||||
});
|
});
|
||||||
|
|
||||||
let last_block_indexer = runtime_wrapped
|
let last_block_indexer_ffi_res = unsafe { query_last_block(blocking_ctx.indexer_ffi()) };
|
||||||
.block_on(ctx.indexer_client().get_last_finalized_block_id())
|
|
||||||
.unwrap();
|
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}");
|
info!("Last block on ind now is {last_block_indexer}");
|
||||||
|
|
||||||
assert!(last_block_indexer > 1);
|
assert!(last_block_indexer > 1);
|
||||||
|
|
||||||
// Getting wide batch to fit all blocks (from latest backwards)
|
let before_ffi = FfiOption::<u64>::from_none();
|
||||||
let mut block_batch = runtime_wrapped
|
let limit = 100;
|
||||||
.block_on(ctx.indexer_client().get_blocks(None, 100))
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// Reverse to check chain consistency from oldest to newest
|
let block_batch_ffi_res =
|
||||||
block_batch.reverse();
|
unsafe { query_block_vec(blocking_ctx.indexer_ffi(), before_ffi, limit) };
|
||||||
|
|
||||||
// Checking chain consistency
|
assert!(block_batch_ffi_res.error.is_ok());
|
||||||
let mut prev_block_hash = block_batch.first().unwrap().header.hash;
|
|
||||||
|
|
||||||
for block in &block_batch[1..] {
|
let block_batch = unsafe { &*block_batch_ffi_res.value };
|
||||||
assert_eq!(block.header.prev_block_hash, prev_block_hash);
|
|
||||||
|
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);
|
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(())
|
Ok(())
|
||||||
@ -82,6 +119,7 @@ fn indexer_ffi_block_batching() -> Result<()> {
|
|||||||
fn indexer_ffi_state_consistency() -> Result<()> {
|
fn indexer_ffi_state_consistency() -> Result<()> {
|
||||||
let mut blocking_ctx = BlockingTestContextFFI::new()?;
|
let mut blocking_ctx = BlockingTestContextFFI::new()?;
|
||||||
let runtime_wrapped = blocking_ctx.runtime_clone();
|
let runtime_wrapped = blocking_ctx.runtime_clone();
|
||||||
|
let indexer_ffi = blocking_ctx.indexer_ffi();
|
||||||
let ctx = blocking_ctx.ctx_mut();
|
let ctx = blocking_ctx.ctx_mut();
|
||||||
|
|
||||||
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
|
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;
|
tokio::time::sleep(std::time::Duration::from_millis(L2_TO_L1_TIMEOUT_MILLIS)).await;
|
||||||
});
|
});
|
||||||
|
|
||||||
let acc1_ind_state = runtime_wrapped.block_on(
|
let acc1_ind_state_ffi =
|
||||||
ctx.indexer_client()
|
unsafe { query_account(indexer_ffi, (&ctx.existing_public_accounts()[0]).into()) };
|
||||||
.get_account(ctx.existing_public_accounts()[0].into()),
|
|
||||||
)?;
|
assert!(acc1_ind_state_ffi.error.is_ok());
|
||||||
let acc2_ind_state = runtime_wrapped.block_on(
|
|
||||||
ctx.indexer_client()
|
let acc1_ind_state_pre = unsafe { &*acc1_ind_state_ffi.value };
|
||||||
.get_account(ctx.existing_public_accounts()[1].into()),
|
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");
|
info!("Checking correct state transition");
|
||||||
let acc1_seq_state =
|
let acc1_seq_state =
|
||||||
@ -208,6 +253,7 @@ fn indexer_ffi_state_consistency() -> Result<()> {
|
|||||||
fn indexer_ffi_state_consistency_with_labels() -> Result<()> {
|
fn indexer_ffi_state_consistency_with_labels() -> Result<()> {
|
||||||
let mut blocking_ctx = BlockingTestContextFFI::new()?;
|
let mut blocking_ctx = BlockingTestContextFFI::new()?;
|
||||||
let runtime_wrapped = blocking_ctx.runtime_clone();
|
let runtime_wrapped = blocking_ctx.runtime_clone();
|
||||||
|
let indexer_ffi = blocking_ctx.indexer_ffi();
|
||||||
let ctx = blocking_ctx.ctx_mut();
|
let ctx = blocking_ctx.ctx_mut();
|
||||||
|
|
||||||
// Assign labels to both accounts
|
// 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;
|
tokio::time::sleep(std::time::Duration::from_millis(L2_TO_L1_TIMEOUT_MILLIS)).await;
|
||||||
});
|
});
|
||||||
|
|
||||||
let acc1_ind_state = runtime_wrapped.block_on(
|
let acc1_ind_state_ffi =
|
||||||
ctx.indexer_client()
|
unsafe { query_account(indexer_ffi, (&ctx.existing_public_accounts()[0]).into()) };
|
||||||
.get_account(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 =
|
let acc1_seq_state =
|
||||||
runtime_wrapped.block_on(sequencer_service_rpc::RpcClient::get_account(
|
runtime_wrapped.block_on(sequencer_service_rpc::RpcClient::get_account(
|
||||||
ctx.sequencer_client(),
|
ctx.sequencer_client(),
|
||||||
|
|||||||
@ -15,6 +15,7 @@ wallet.workspace = true
|
|||||||
nssa.workspace = true
|
nssa.workspace = true
|
||||||
nssa_core.workspace = true
|
nssa_core.workspace = true
|
||||||
sequencer_service_rpc = { workspace = true, features = ["client"] }
|
sequencer_service_rpc = { workspace = true, features = ["client"] }
|
||||||
|
|
||||||
tokio.workspace = true
|
tokio.workspace = true
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user