Add federation registry, replay, and ingest
This commit is contained in:
parent
f707244888
commit
5a0a2f80c7
|
|
@ -122,6 +122,12 @@ set(AMDUAT_TGK_SRCS
|
||||||
src/tgk_stack/prov/prov.c
|
src/tgk_stack/prov/prov.c
|
||||||
)
|
)
|
||||||
|
|
||||||
|
set(AMDUAT_FED_SRCS
|
||||||
|
src/near_core/fed/registry.c
|
||||||
|
src/near_core/fed/replay.c
|
||||||
|
src/near_core/fed/ingest.c
|
||||||
|
)
|
||||||
|
|
||||||
set(AMDUAT_ASL_STORE_FS_SRCS
|
set(AMDUAT_ASL_STORE_FS_SRCS
|
||||||
src/adapters/asl_store_fs/asl_store_fs.c
|
src/adapters/asl_store_fs/asl_store_fs.c
|
||||||
src/adapters/asl_store_fs/asl_store_fs_layout.c
|
src/adapters/asl_store_fs/asl_store_fs_layout.c
|
||||||
|
|
@ -169,6 +175,9 @@ amduat_link(pel amduat_asl amduat_enc amduat_hash_asl1 amduat_util)
|
||||||
amduat_add_lib(tgk SRCS ${AMDUAT_TGK_SRCS})
|
amduat_add_lib(tgk SRCS ${AMDUAT_TGK_SRCS})
|
||||||
amduat_link(tgk amduat_asl amduat_enc amduat_hash_asl1 amduat_util)
|
amduat_link(tgk amduat_asl amduat_enc amduat_hash_asl1 amduat_util)
|
||||||
|
|
||||||
|
amduat_add_lib(fed SRCS ${AMDUAT_FED_SRCS})
|
||||||
|
amduat_link(fed amduat_asl)
|
||||||
|
|
||||||
amduat_add_lib(asl_store_fs SRCS ${AMDUAT_ASL_STORE_FS_SRCS})
|
amduat_add_lib(asl_store_fs SRCS ${AMDUAT_ASL_STORE_FS_SRCS})
|
||||||
amduat_link(asl_store_fs amduat_asl amduat_enc amduat_hash_asl1 amduat_util)
|
amduat_link(asl_store_fs amduat_asl amduat_enc amduat_hash_asl1 amduat_util)
|
||||||
target_compile_definitions(amduat_asl_store_fs_obj PRIVATE _POSIX_C_SOURCE=200809L)
|
target_compile_definitions(amduat_asl_store_fs_obj PRIVATE _POSIX_C_SOURCE=200809L)
|
||||||
|
|
@ -533,3 +542,33 @@ target_link_libraries(amduat_test_pel_queue
|
||||||
PRIVATE amduat_pel
|
PRIVATE amduat_pel
|
||||||
)
|
)
|
||||||
add_test(NAME pel_queue COMMAND amduat_test_pel_queue)
|
add_test(NAME pel_queue COMMAND amduat_test_pel_queue)
|
||||||
|
|
||||||
|
add_executable(amduat_test_fed_registry tests/fed/test_fed_registry.c)
|
||||||
|
target_include_directories(amduat_test_fed_registry
|
||||||
|
PRIVATE ${AMDUAT_INTERNAL_DIR}
|
||||||
|
PRIVATE ${AMDUAT_INCLUDE_DIR}
|
||||||
|
)
|
||||||
|
target_link_libraries(amduat_test_fed_registry
|
||||||
|
PRIVATE amduat_fed
|
||||||
|
)
|
||||||
|
add_test(NAME fed_registry COMMAND amduat_test_fed_registry)
|
||||||
|
|
||||||
|
add_executable(amduat_test_fed_replay tests/fed/test_fed_replay.c)
|
||||||
|
target_include_directories(amduat_test_fed_replay
|
||||||
|
PRIVATE ${AMDUAT_INTERNAL_DIR}
|
||||||
|
PRIVATE ${AMDUAT_INCLUDE_DIR}
|
||||||
|
)
|
||||||
|
target_link_libraries(amduat_test_fed_replay
|
||||||
|
PRIVATE amduat_fed
|
||||||
|
)
|
||||||
|
add_test(NAME fed_replay COMMAND amduat_test_fed_replay)
|
||||||
|
|
||||||
|
add_executable(amduat_test_fed_ingest tests/fed/test_fed_ingest.c)
|
||||||
|
target_include_directories(amduat_test_fed_ingest
|
||||||
|
PRIVATE ${AMDUAT_INTERNAL_DIR}
|
||||||
|
PRIVATE ${AMDUAT_INCLUDE_DIR}
|
||||||
|
)
|
||||||
|
target_link_libraries(amduat_test_fed_ingest
|
||||||
|
PRIVATE amduat_fed
|
||||||
|
)
|
||||||
|
add_test(NAME fed_ingest COMMAND amduat_test_fed_ingest)
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,21 @@ The following are explicitly out of scope for core:
|
||||||
- Transport protocols (HTTP, IPC, gossip).
|
- Transport protocols (HTTP, IPC, gossip).
|
||||||
- Peer discovery and operational orchestration.
|
- Peer discovery and operational orchestration.
|
||||||
- Admin UX and deployment wiring.
|
- Admin UX and deployment wiring.
|
||||||
|
- Admission workflows, auth, and retries/backoff.
|
||||||
|
- Cache policy knobs (fetch timing, eviction, prefetch).
|
||||||
|
- Operational concerns (metrics, admin endpoints).
|
||||||
|
- Policy evaluation and per-record filtering decisions.
|
||||||
|
|
||||||
|
## Layering note
|
||||||
|
|
||||||
|
Core provides deterministic federation semantics and view construction only.
|
||||||
|
Middle-layer components are responsible for transport, admission workflows,
|
||||||
|
policy evaluation (including per-record filtering), caching strategies, and
|
||||||
|
operational wiring.
|
||||||
|
|
||||||
|
Definition:
|
||||||
|
- Middle layer: the daemon/service boundary around core logic that owns
|
||||||
|
network transport, admission workflows, and operational policy.
|
||||||
|
|
||||||
## Responsibilities
|
## Responsibilities
|
||||||
|
|
||||||
|
|
@ -64,6 +79,13 @@ The following are explicitly out of scope for core:
|
||||||
## Data model (suggested)
|
## Data model (suggested)
|
||||||
|
|
||||||
```c
|
```c
|
||||||
|
typedef enum {
|
||||||
|
AMDUAT_FED_REC_ARTIFACT = 0,
|
||||||
|
AMDUAT_FED_REC_PER = 1,
|
||||||
|
AMDUAT_FED_REC_TGK_EDGE = 2,
|
||||||
|
AMDUAT_FED_REC_TOMBSTONE = 3
|
||||||
|
} amduat_fed_record_type_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint32_t domain_id;
|
uint32_t domain_id;
|
||||||
uint64_t snapshot_id;
|
uint64_t snapshot_id;
|
||||||
|
|
@ -72,6 +94,8 @@ typedef struct {
|
||||||
uint8_t admitted; // boolean
|
uint8_t admitted; // boolean
|
||||||
uint8_t policy_ok; // boolean
|
uint8_t policy_ok; // boolean
|
||||||
uint8_t reserved[6];
|
uint8_t reserved[6];
|
||||||
|
amduat_hash_id_t policy_hash_id;
|
||||||
|
amduat_octets_t policy_hash;
|
||||||
} amduat_fed_domain_state_t;
|
} amduat_fed_domain_state_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|
@ -82,19 +106,38 @@ typedef struct {
|
||||||
uint32_t source_domain;
|
uint32_t source_domain;
|
||||||
} amduat_fed_record_meta_t;
|
} amduat_fed_record_meta_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
amduat_fed_record_type_t type;
|
||||||
|
union {
|
||||||
|
amduat_asl_artifact_key_t artifact_key;
|
||||||
|
amduat_asl_tgk_edge_key_t tgk_edge_key;
|
||||||
|
amduat_asl_per_key_t per_key;
|
||||||
|
amduat_asl_artifact_key_t tombstone_key; // key being removed
|
||||||
|
} id;
|
||||||
|
} amduat_fed_record_id_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
amduat_fed_record_meta_t meta;
|
amduat_fed_record_meta_t meta;
|
||||||
amduat_asl_artifact_key_t key;
|
amduat_fed_record_id_t id;
|
||||||
amduat_asl_artifact_location_t loc;
|
amduat_asl_artifact_location_t loc;
|
||||||
uint64_t logseq;
|
uint64_t logseq;
|
||||||
uint64_t snapshot_id;
|
uint64_t snapshot_id;
|
||||||
uint64_t log_prefix;
|
uint64_t log_prefix;
|
||||||
} amduat_fed_index_record_t;
|
} amduat_fed_index_record_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
amduat_fed_record_id_t id;
|
||||||
|
uint32_t reason_code; // policy-specific; 0 if unknown
|
||||||
|
} amduat_fed_policy_deny_t;
|
||||||
```
|
```
|
||||||
|
|
||||||
Notes:
|
Notes:
|
||||||
- Imported records MUST retain domain_id and cross-domain source metadata.
|
- Imported records MUST retain domain_id and cross-domain source metadata.
|
||||||
- Tombstones must retain domain_id/visibility for domain-local shadowing.
|
- Tombstones must retain domain_id/visibility for domain-local shadowing.
|
||||||
|
- Each record MUST include a record type and canonical identity for deterministic
|
||||||
|
replay across artifacts, PERs, TGK edges, and tombstones.
|
||||||
|
- PER/TGK canonical identities are currently represented by ASL references
|
||||||
|
(artifact IDs); no separate edge/PER key types exist yet.
|
||||||
|
|
||||||
## Core API sketch
|
## Core API sketch
|
||||||
|
|
||||||
|
|
@ -111,6 +154,7 @@ bool amduat_fed_set_domain_state(amduat_fed_registry_t *, domain_id,
|
||||||
snapshot_id, log_prefix);
|
snapshot_id, log_prefix);
|
||||||
|
|
||||||
// Ingest published records for a domain (already transported).
|
// Ingest published records for a domain (already transported).
|
||||||
|
// Each record MUST include its type and canonical identity in the id field.
|
||||||
bool amduat_fed_ingest_records(amduat_fed_registry_t *, domain_id,
|
bool amduat_fed_ingest_records(amduat_fed_registry_t *, domain_id,
|
||||||
const amduat_fed_index_record_t *records,
|
const amduat_fed_index_record_t *records,
|
||||||
size_t count);
|
size_t count);
|
||||||
|
|
@ -131,6 +175,9 @@ Notes:
|
||||||
- Transport fetch is not part of resolve; it only consumes ingested records.
|
- Transport fetch is not part of resolve; it only consumes ingested records.
|
||||||
- The daemon can choose to fetch missing bytes when resolve reports a remote
|
- The daemon can choose to fetch missing bytes when resolve reports a remote
|
||||||
location but local bytes are absent.
|
location but local bytes are absent.
|
||||||
|
- If per-record filtering is enabled, it is applied during ingest or view build
|
||||||
|
and any denials are recorded in view metadata (e.g., a table of
|
||||||
|
amduat_fed_policy_deny_t).
|
||||||
|
|
||||||
## Replay and view construction
|
## Replay and view construction
|
||||||
|
|
||||||
|
|
@ -153,6 +200,12 @@ Federation view storage MAY be:
|
||||||
If remote bytes are fetched, they MUST be written to a cache store that is
|
If remote bytes are fetched, they MUST be written to a cache store that is
|
||||||
logically separate from the authoritative local store (policy-controlled).
|
logically separate from the authoritative local store (policy-controlled).
|
||||||
|
|
||||||
|
## Policy gating
|
||||||
|
|
||||||
|
- Admission gating is per-domain.
|
||||||
|
- Per-record filtering is optional and MUST be an explicit, deterministic
|
||||||
|
policy layer if enabled.
|
||||||
|
|
||||||
## Error reporting
|
## Error reporting
|
||||||
|
|
||||||
Core resolve should distinguish:
|
Core resolve should distinguish:
|
||||||
|
|
@ -161,6 +214,12 @@ Core resolve should distinguish:
|
||||||
- INTEGRITY_ERROR (hash mismatch on bytes)
|
- INTEGRITY_ERROR (hash mismatch on bytes)
|
||||||
- POLICY_DENIED (domain admitted but record filtered by policy)
|
- POLICY_DENIED (domain admitted but record filtered by policy)
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
- When per-record filtering is enabled, POLICY_DENIED SHOULD surface at ingest
|
||||||
|
or view-build time by excluding filtered records from the view and recording
|
||||||
|
the denial in view metadata. Resolve MAY return POLICY_DENIED only when such a
|
||||||
|
denial is recorded for the queried key; otherwise it MUST return NOT_FOUND.
|
||||||
|
|
||||||
## Tests (minimal)
|
## Tests (minimal)
|
||||||
|
|
||||||
1) Replay ordering determinism across two domains with interleaved logseq.
|
1) Replay ordering determinism across two domains with interleaved logseq.
|
||||||
|
|
|
||||||
28
include/amduat/fed/ingest.h
Normal file
28
include/amduat/fed/ingest.h
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
#ifndef AMDUAT_FED_INGEST_H
|
||||||
|
#define AMDUAT_FED_INGEST_H
|
||||||
|
|
||||||
|
#include "amduat/fed/replay.h"
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
AMDUAT_FED_INGEST_OK = 0,
|
||||||
|
AMDUAT_FED_INGEST_ERR_INVALID = 1,
|
||||||
|
AMDUAT_FED_INGEST_ERR_CONFLICT = 2
|
||||||
|
} amduat_fed_ingest_error_t;
|
||||||
|
|
||||||
|
amduat_fed_ingest_error_t amduat_fed_ingest_validate(
|
||||||
|
const amduat_fed_record_t *records,
|
||||||
|
size_t count,
|
||||||
|
size_t *out_error_index,
|
||||||
|
size_t *out_conflict_index);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} /* extern "C" */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* AMDUAT_FED_INGEST_H */
|
||||||
82
include/amduat/fed/registry.h
Normal file
82
include/amduat/fed/registry.h
Normal file
|
|
@ -0,0 +1,82 @@
|
||||||
|
#ifndef AMDUAT_FED_REGISTRY_H
|
||||||
|
#define AMDUAT_FED_REGISTRY_H
|
||||||
|
|
||||||
|
#include "amduat/asl/core.h"
|
||||||
|
#include "amduat/asl/store.h"
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t domain_id;
|
||||||
|
uint64_t snapshot_id;
|
||||||
|
uint64_t log_prefix;
|
||||||
|
uint64_t last_logseq;
|
||||||
|
uint8_t admitted;
|
||||||
|
uint8_t policy_ok;
|
||||||
|
uint8_t reserved[6];
|
||||||
|
amduat_hash_id_t policy_hash_id;
|
||||||
|
amduat_octets_t policy_hash; /* Empty when unknown. */
|
||||||
|
} amduat_fed_domain_state_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
amduat_fed_domain_state_t *states;
|
||||||
|
size_t len;
|
||||||
|
size_t cap;
|
||||||
|
bool owns_states;
|
||||||
|
} amduat_fed_registry_value_t;
|
||||||
|
|
||||||
|
void amduat_fed_registry_value_init(amduat_fed_registry_value_t *value,
|
||||||
|
amduat_fed_domain_state_t *states,
|
||||||
|
size_t cap);
|
||||||
|
|
||||||
|
bool amduat_fed_registry_value_insert(amduat_fed_registry_value_t *value,
|
||||||
|
amduat_fed_domain_state_t state);
|
||||||
|
|
||||||
|
const amduat_fed_domain_state_t *amduat_fed_registry_value_lookup(
|
||||||
|
const amduat_fed_registry_value_t *value,
|
||||||
|
uint32_t domain_id);
|
||||||
|
|
||||||
|
void amduat_fed_registry_value_free(amduat_fed_registry_value_t *value);
|
||||||
|
|
||||||
|
bool amduat_fed_registry_encode(const amduat_fed_registry_value_t *value,
|
||||||
|
amduat_octets_t *out_bytes);
|
||||||
|
|
||||||
|
bool amduat_fed_registry_decode(amduat_octets_t bytes,
|
||||||
|
amduat_fed_registry_value_t *out_value);
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
AMDUAT_FED_REGISTRY_OK = 0,
|
||||||
|
AMDUAT_FED_REGISTRY_ERR_CODEC = 1,
|
||||||
|
AMDUAT_FED_REGISTRY_ERR_STORE = 2
|
||||||
|
} amduat_fed_registry_error_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
amduat_asl_store_t *store;
|
||||||
|
} amduat_fed_registry_store_t;
|
||||||
|
|
||||||
|
void amduat_fed_registry_store_init(amduat_fed_registry_store_t *reg,
|
||||||
|
amduat_asl_store_t *store);
|
||||||
|
|
||||||
|
amduat_fed_registry_error_t amduat_fed_registry_store_put(
|
||||||
|
amduat_fed_registry_store_t *reg,
|
||||||
|
const amduat_fed_registry_value_t *value,
|
||||||
|
amduat_reference_t *out_ref,
|
||||||
|
amduat_asl_store_error_t *out_store_err);
|
||||||
|
|
||||||
|
amduat_fed_registry_error_t amduat_fed_registry_store_get(
|
||||||
|
amduat_fed_registry_store_t *reg,
|
||||||
|
amduat_reference_t ref,
|
||||||
|
amduat_fed_registry_value_t *out_value,
|
||||||
|
amduat_asl_store_error_t *out_store_err);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} /* extern "C" */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* AMDUAT_FED_REGISTRY_H */
|
||||||
62
include/amduat/fed/replay.h
Normal file
62
include/amduat/fed/replay.h
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
#ifndef AMDUAT_FED_REPLAY_H
|
||||||
|
#define AMDUAT_FED_REPLAY_H
|
||||||
|
|
||||||
|
#include "amduat/asl/core.h"
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
AMDUAT_FED_REC_ARTIFACT = 0,
|
||||||
|
AMDUAT_FED_REC_PER = 1,
|
||||||
|
AMDUAT_FED_REC_TGK_EDGE = 2,
|
||||||
|
AMDUAT_FED_REC_TOMBSTONE = 3
|
||||||
|
} amduat_fed_record_type_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t domain_id;
|
||||||
|
uint8_t visibility;
|
||||||
|
uint8_t has_source;
|
||||||
|
uint16_t reserved0;
|
||||||
|
uint32_t source_domain;
|
||||||
|
} amduat_fed_record_meta_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
amduat_fed_record_type_t type;
|
||||||
|
amduat_reference_t ref;
|
||||||
|
} amduat_fed_record_id_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
amduat_fed_record_meta_t meta;
|
||||||
|
amduat_fed_record_id_t id;
|
||||||
|
uint64_t logseq;
|
||||||
|
uint64_t snapshot_id;
|
||||||
|
uint64_t log_prefix;
|
||||||
|
} amduat_fed_record_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
amduat_fed_record_t *records;
|
||||||
|
size_t len;
|
||||||
|
} amduat_fed_replay_view_t;
|
||||||
|
|
||||||
|
bool amduat_fed_record_validate(const amduat_fed_record_t *record);
|
||||||
|
|
||||||
|
bool amduat_fed_replay_domain(const amduat_fed_record_t *records,
|
||||||
|
size_t count,
|
||||||
|
uint32_t domain_id,
|
||||||
|
uint64_t snapshot_id,
|
||||||
|
uint64_t log_prefix,
|
||||||
|
amduat_fed_replay_view_t *out_view);
|
||||||
|
|
||||||
|
void amduat_fed_replay_view_free(amduat_fed_replay_view_t *view);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} /* extern "C" */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* AMDUAT_FED_REPLAY_H */
|
||||||
72
src/near_core/fed/ingest.c
Normal file
72
src/near_core/fed/ingest.c
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
#include "amduat/fed/ingest.h"
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
static bool amduat_fed_record_id_eq(const amduat_fed_record_id_t *a,
|
||||||
|
const amduat_fed_record_id_t *b) {
|
||||||
|
if (a->type != b->type) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return amduat_reference_eq(a->ref, b->ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool amduat_fed_record_equivalent(const amduat_fed_record_t *a,
|
||||||
|
const amduat_fed_record_t *b) {
|
||||||
|
return a->meta.domain_id == b->meta.domain_id &&
|
||||||
|
a->meta.visibility == b->meta.visibility &&
|
||||||
|
a->meta.has_source == b->meta.has_source &&
|
||||||
|
a->meta.source_domain == b->meta.source_domain &&
|
||||||
|
a->logseq == b->logseq &&
|
||||||
|
a->snapshot_id == b->snapshot_id &&
|
||||||
|
a->log_prefix == b->log_prefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
amduat_fed_ingest_error_t amduat_fed_ingest_validate(
|
||||||
|
const amduat_fed_record_t *records,
|
||||||
|
size_t count,
|
||||||
|
size_t *out_error_index,
|
||||||
|
size_t *out_conflict_index) {
|
||||||
|
size_t i;
|
||||||
|
size_t j;
|
||||||
|
|
||||||
|
if (out_error_index != NULL) {
|
||||||
|
*out_error_index = (size_t)-1;
|
||||||
|
}
|
||||||
|
if (out_conflict_index != NULL) {
|
||||||
|
*out_conflict_index = (size_t)-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (records == NULL && count != 0u) {
|
||||||
|
return AMDUAT_FED_INGEST_ERR_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < count; ++i) {
|
||||||
|
const amduat_fed_record_t *record = &records[i];
|
||||||
|
|
||||||
|
if (!amduat_fed_record_validate(record)) {
|
||||||
|
if (out_error_index != NULL) {
|
||||||
|
*out_error_index = i;
|
||||||
|
}
|
||||||
|
return AMDUAT_FED_INGEST_ERR_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (j = 0; j < i; ++j) {
|
||||||
|
const amduat_fed_record_t *other = &records[j];
|
||||||
|
|
||||||
|
if (!amduat_fed_record_id_eq(&record->id, &other->id)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!amduat_fed_record_equivalent(record, other)) {
|
||||||
|
if (out_error_index != NULL) {
|
||||||
|
*out_error_index = i;
|
||||||
|
}
|
||||||
|
if (out_conflict_index != NULL) {
|
||||||
|
*out_conflict_index = j;
|
||||||
|
}
|
||||||
|
return AMDUAT_FED_INGEST_ERR_CONFLICT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return AMDUAT_FED_INGEST_OK;
|
||||||
|
}
|
||||||
416
src/near_core/fed/registry.c
Normal file
416
src/near_core/fed/registry.c
Normal file
|
|
@ -0,0 +1,416 @@
|
||||||
|
#include "amduat/fed/registry.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
enum {
|
||||||
|
AMDUAT_FED_REGISTRY_VERSION = 1,
|
||||||
|
AMDUAT_FED_REGISTRY_HEADER_LEN = 8,
|
||||||
|
AMDUAT_FED_REGISTRY_ENTRY_FIXED_LEN = 42
|
||||||
|
};
|
||||||
|
|
||||||
|
static void amduat_fed_store_u16_be(uint8_t *out, uint16_t value) {
|
||||||
|
out[0] = (uint8_t)((value >> 8) & 0xffu);
|
||||||
|
out[1] = (uint8_t)(value & 0xffu);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void amduat_fed_store_u32_be(uint8_t *out, uint32_t value) {
|
||||||
|
out[0] = (uint8_t)((value >> 24) & 0xffu);
|
||||||
|
out[1] = (uint8_t)((value >> 16) & 0xffu);
|
||||||
|
out[2] = (uint8_t)((value >> 8) & 0xffu);
|
||||||
|
out[3] = (uint8_t)(value & 0xffu);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void amduat_fed_store_u64_be(uint8_t *out, uint64_t value) {
|
||||||
|
out[0] = (uint8_t)((value >> 56) & 0xffu);
|
||||||
|
out[1] = (uint8_t)((value >> 48) & 0xffu);
|
||||||
|
out[2] = (uint8_t)((value >> 40) & 0xffu);
|
||||||
|
out[3] = (uint8_t)((value >> 32) & 0xffu);
|
||||||
|
out[4] = (uint8_t)((value >> 24) & 0xffu);
|
||||||
|
out[5] = (uint8_t)((value >> 16) & 0xffu);
|
||||||
|
out[6] = (uint8_t)((value >> 8) & 0xffu);
|
||||||
|
out[7] = (uint8_t)(value & 0xffu);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint16_t amduat_fed_load_u16_be(const uint8_t *data) {
|
||||||
|
return ((uint16_t)data[0] << 8) | (uint16_t)data[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t amduat_fed_load_u32_be(const uint8_t *data) {
|
||||||
|
return ((uint32_t)data[0] << 24) | ((uint32_t)data[1] << 16) |
|
||||||
|
((uint32_t)data[2] << 8) | (uint32_t)data[3];
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t amduat_fed_load_u64_be(const uint8_t *data) {
|
||||||
|
return ((uint64_t)data[0] << 56) | ((uint64_t)data[1] << 48) |
|
||||||
|
((uint64_t)data[2] << 40) | ((uint64_t)data[3] << 32) |
|
||||||
|
((uint64_t)data[4] << 24) | ((uint64_t)data[5] << 16) |
|
||||||
|
((uint64_t)data[6] << 8) | (uint64_t)data[7];
|
||||||
|
}
|
||||||
|
|
||||||
|
void amduat_fed_registry_value_init(amduat_fed_registry_value_t *value,
|
||||||
|
amduat_fed_domain_state_t *states,
|
||||||
|
size_t cap) {
|
||||||
|
if (value == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
value->states = states;
|
||||||
|
value->len = 0;
|
||||||
|
value->cap = cap;
|
||||||
|
value->owns_states = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool amduat_fed_registry_value_insert(amduat_fed_registry_value_t *value,
|
||||||
|
amduat_fed_domain_state_t state) {
|
||||||
|
size_t left;
|
||||||
|
size_t right;
|
||||||
|
|
||||||
|
if (value == NULL || value->states == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (value->len >= value->cap) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
left = 0;
|
||||||
|
right = value->len;
|
||||||
|
while (left < right) {
|
||||||
|
size_t mid;
|
||||||
|
uint32_t current_id;
|
||||||
|
|
||||||
|
mid = left + (right - left) / 2;
|
||||||
|
current_id = value->states[mid].domain_id;
|
||||||
|
if (state.domain_id == current_id) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (state.domain_id < current_id) {
|
||||||
|
right = mid;
|
||||||
|
} else {
|
||||||
|
left = mid + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (left < value->len) {
|
||||||
|
memmove(&value->states[left + 1],
|
||||||
|
&value->states[left],
|
||||||
|
(value->len - left) * sizeof(*value->states));
|
||||||
|
}
|
||||||
|
value->states[left] = state;
|
||||||
|
value->len += 1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const amduat_fed_domain_state_t *amduat_fed_registry_value_lookup(
|
||||||
|
const amduat_fed_registry_value_t *value,
|
||||||
|
uint32_t domain_id) {
|
||||||
|
size_t left;
|
||||||
|
size_t right;
|
||||||
|
|
||||||
|
if (value == NULL || value->states == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
left = 0;
|
||||||
|
right = value->len;
|
||||||
|
while (left < right) {
|
||||||
|
size_t mid;
|
||||||
|
uint32_t current_id;
|
||||||
|
|
||||||
|
mid = left + (right - left) / 2;
|
||||||
|
current_id = value->states[mid].domain_id;
|
||||||
|
if (domain_id == current_id) {
|
||||||
|
return &value->states[mid];
|
||||||
|
}
|
||||||
|
if (domain_id < current_id) {
|
||||||
|
right = mid;
|
||||||
|
} else {
|
||||||
|
left = mid + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void amduat_fed_registry_value_free(amduat_fed_registry_value_t *value) {
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
if (value == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value->states != NULL) {
|
||||||
|
for (i = 0; i < value->len; ++i) {
|
||||||
|
amduat_octets_free(&value->states[i].policy_hash);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value->owns_states && value->states != NULL) {
|
||||||
|
free(value->states);
|
||||||
|
value->states = NULL;
|
||||||
|
value->cap = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
value->len = 0;
|
||||||
|
value->owns_states = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool amduat_fed_registry_encode(const amduat_fed_registry_value_t *value,
|
||||||
|
amduat_octets_t *out_bytes) {
|
||||||
|
size_t i;
|
||||||
|
size_t total_len;
|
||||||
|
size_t offset;
|
||||||
|
uint8_t *buffer;
|
||||||
|
|
||||||
|
if (out_bytes == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
out_bytes->data = NULL;
|
||||||
|
out_bytes->len = 0;
|
||||||
|
|
||||||
|
if (value == NULL || value->states == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (value->len > UINT32_MAX) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
total_len = AMDUAT_FED_REGISTRY_HEADER_LEN;
|
||||||
|
for (i = 0; i < value->len; ++i) {
|
||||||
|
const amduat_fed_domain_state_t *state;
|
||||||
|
size_t entry_len;
|
||||||
|
|
||||||
|
state = &value->states[i];
|
||||||
|
if (state->policy_hash.len > UINT32_MAX) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (state->policy_hash.len != 0u && state->policy_hash.data == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry_len = AMDUAT_FED_REGISTRY_ENTRY_FIXED_LEN + state->policy_hash.len;
|
||||||
|
if (entry_len > SIZE_MAX - total_len) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
total_len += entry_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer = (uint8_t *)malloc(total_len);
|
||||||
|
if (buffer == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
offset = 0;
|
||||||
|
amduat_fed_store_u32_be(buffer + offset, AMDUAT_FED_REGISTRY_VERSION);
|
||||||
|
offset += 4;
|
||||||
|
amduat_fed_store_u32_be(buffer + offset, (uint32_t)value->len);
|
||||||
|
offset += 4;
|
||||||
|
|
||||||
|
for (i = 0; i < value->len; ++i) {
|
||||||
|
const amduat_fed_domain_state_t *state;
|
||||||
|
|
||||||
|
state = &value->states[i];
|
||||||
|
amduat_fed_store_u32_be(buffer + offset, state->domain_id);
|
||||||
|
offset += 4;
|
||||||
|
amduat_fed_store_u64_be(buffer + offset, state->snapshot_id);
|
||||||
|
offset += 8;
|
||||||
|
amduat_fed_store_u64_be(buffer + offset, state->log_prefix);
|
||||||
|
offset += 8;
|
||||||
|
amduat_fed_store_u64_be(buffer + offset, state->last_logseq);
|
||||||
|
offset += 8;
|
||||||
|
buffer[offset++] = state->admitted;
|
||||||
|
buffer[offset++] = state->policy_ok;
|
||||||
|
memcpy(buffer + offset, state->reserved, sizeof(state->reserved));
|
||||||
|
offset += sizeof(state->reserved);
|
||||||
|
amduat_fed_store_u16_be(buffer + offset, state->policy_hash_id);
|
||||||
|
offset += 2;
|
||||||
|
amduat_fed_store_u32_be(buffer + offset,
|
||||||
|
(uint32_t)state->policy_hash.len);
|
||||||
|
offset += 4;
|
||||||
|
if (state->policy_hash.len != 0u) {
|
||||||
|
memcpy(buffer + offset,
|
||||||
|
state->policy_hash.data,
|
||||||
|
state->policy_hash.len);
|
||||||
|
offset += state->policy_hash.len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out_bytes->data = buffer;
|
||||||
|
out_bytes->len = total_len;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool amduat_fed_registry_decode(amduat_octets_t bytes,
|
||||||
|
amduat_fed_registry_value_t *out_value) {
|
||||||
|
uint32_t version;
|
||||||
|
uint32_t count;
|
||||||
|
size_t offset;
|
||||||
|
size_t i;
|
||||||
|
amduat_fed_registry_value_t value;
|
||||||
|
|
||||||
|
if (out_value == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (bytes.len != 0u && bytes.data == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (bytes.len < AMDUAT_FED_REGISTRY_HEADER_LEN) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
offset = 0;
|
||||||
|
version = amduat_fed_load_u32_be(bytes.data + offset);
|
||||||
|
offset += 4;
|
||||||
|
if (version != AMDUAT_FED_REGISTRY_VERSION) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
count = amduat_fed_load_u32_be(bytes.data + offset);
|
||||||
|
offset += 4;
|
||||||
|
|
||||||
|
value = *out_value;
|
||||||
|
if (value.states == NULL) {
|
||||||
|
if (count != 0u) {
|
||||||
|
value.states =
|
||||||
|
(amduat_fed_domain_state_t *)calloc(count, sizeof(*value.states));
|
||||||
|
if (value.states == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
value.cap = count;
|
||||||
|
value.owns_states = true;
|
||||||
|
} else {
|
||||||
|
if (value.cap < count) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
value.owns_states = false;
|
||||||
|
}
|
||||||
|
value.len = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < count; ++i) {
|
||||||
|
amduat_fed_domain_state_t state;
|
||||||
|
uint32_t policy_hash_len;
|
||||||
|
|
||||||
|
if (bytes.len - offset < AMDUAT_FED_REGISTRY_ENTRY_FIXED_LEN) {
|
||||||
|
amduat_fed_registry_value_free(&value);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&state, 0, sizeof(state));
|
||||||
|
state.domain_id = amduat_fed_load_u32_be(bytes.data + offset);
|
||||||
|
offset += 4;
|
||||||
|
state.snapshot_id = amduat_fed_load_u64_be(bytes.data + offset);
|
||||||
|
offset += 8;
|
||||||
|
state.log_prefix = amduat_fed_load_u64_be(bytes.data + offset);
|
||||||
|
offset += 8;
|
||||||
|
state.last_logseq = amduat_fed_load_u64_be(bytes.data + offset);
|
||||||
|
offset += 8;
|
||||||
|
state.admitted = bytes.data[offset++];
|
||||||
|
state.policy_ok = bytes.data[offset++];
|
||||||
|
memcpy(state.reserved, bytes.data + offset, sizeof(state.reserved));
|
||||||
|
offset += sizeof(state.reserved);
|
||||||
|
state.policy_hash_id = amduat_fed_load_u16_be(bytes.data + offset);
|
||||||
|
offset += 2;
|
||||||
|
policy_hash_len = amduat_fed_load_u32_be(bytes.data + offset);
|
||||||
|
offset += 4;
|
||||||
|
|
||||||
|
if (policy_hash_len > bytes.len - offset) {
|
||||||
|
amduat_fed_registry_value_free(&value);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (policy_hash_len != 0u) {
|
||||||
|
uint8_t *policy_bytes;
|
||||||
|
|
||||||
|
policy_bytes = (uint8_t *)malloc(policy_hash_len);
|
||||||
|
if (policy_bytes == NULL) {
|
||||||
|
amduat_fed_registry_value_free(&value);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
memcpy(policy_bytes, bytes.data + offset, policy_hash_len);
|
||||||
|
state.policy_hash = amduat_octets(policy_bytes, policy_hash_len);
|
||||||
|
offset += policy_hash_len;
|
||||||
|
} else {
|
||||||
|
state.policy_hash = amduat_octets(NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
value.states[i] = state;
|
||||||
|
value.len += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
*out_value = value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void amduat_fed_registry_store_init(amduat_fed_registry_store_t *reg,
|
||||||
|
amduat_asl_store_t *store) {
|
||||||
|
if (reg == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
reg->store = store;
|
||||||
|
}
|
||||||
|
|
||||||
|
amduat_fed_registry_error_t amduat_fed_registry_store_put(
|
||||||
|
amduat_fed_registry_store_t *reg,
|
||||||
|
const amduat_fed_registry_value_t *value,
|
||||||
|
amduat_reference_t *out_ref,
|
||||||
|
amduat_asl_store_error_t *out_store_err) {
|
||||||
|
amduat_octets_t bytes;
|
||||||
|
amduat_artifact_t artifact;
|
||||||
|
amduat_asl_store_error_t store_err;
|
||||||
|
|
||||||
|
if (out_store_err != NULL) {
|
||||||
|
*out_store_err = AMDUAT_ASL_STORE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reg == NULL || reg->store == NULL) {
|
||||||
|
return AMDUAT_FED_REGISTRY_ERR_STORE;
|
||||||
|
}
|
||||||
|
if (!amduat_fed_registry_encode(value, &bytes)) {
|
||||||
|
return AMDUAT_FED_REGISTRY_ERR_CODEC;
|
||||||
|
}
|
||||||
|
|
||||||
|
artifact = amduat_artifact(bytes);
|
||||||
|
store_err = amduat_asl_store_put(reg->store, artifact, out_ref);
|
||||||
|
amduat_octets_free(&bytes);
|
||||||
|
if (store_err != AMDUAT_ASL_STORE_OK) {
|
||||||
|
if (out_store_err != NULL) {
|
||||||
|
*out_store_err = store_err;
|
||||||
|
}
|
||||||
|
return AMDUAT_FED_REGISTRY_ERR_STORE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return AMDUAT_FED_REGISTRY_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
amduat_fed_registry_error_t amduat_fed_registry_store_get(
|
||||||
|
amduat_fed_registry_store_t *reg,
|
||||||
|
amduat_reference_t ref,
|
||||||
|
amduat_fed_registry_value_t *out_value,
|
||||||
|
amduat_asl_store_error_t *out_store_err) {
|
||||||
|
amduat_artifact_t artifact;
|
||||||
|
amduat_asl_store_error_t store_err;
|
||||||
|
bool ok;
|
||||||
|
|
||||||
|
if (out_store_err != NULL) {
|
||||||
|
*out_store_err = AMDUAT_ASL_STORE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reg == NULL || reg->store == NULL || out_value == NULL) {
|
||||||
|
return AMDUAT_FED_REGISTRY_ERR_STORE;
|
||||||
|
}
|
||||||
|
|
||||||
|
store_err = amduat_asl_store_get(reg->store, ref, &artifact);
|
||||||
|
if (store_err != AMDUAT_ASL_STORE_OK) {
|
||||||
|
if (out_store_err != NULL) {
|
||||||
|
*out_store_err = store_err;
|
||||||
|
}
|
||||||
|
return AMDUAT_FED_REGISTRY_ERR_STORE;
|
||||||
|
}
|
||||||
|
|
||||||
|
ok = amduat_fed_registry_decode(artifact.bytes, out_value);
|
||||||
|
amduat_artifact_free(&artifact);
|
||||||
|
if (!ok) {
|
||||||
|
return AMDUAT_FED_REGISTRY_ERR_CODEC;
|
||||||
|
}
|
||||||
|
|
||||||
|
return AMDUAT_FED_REGISTRY_OK;
|
||||||
|
}
|
||||||
295
src/near_core/fed/replay.c
Normal file
295
src/near_core/fed/replay.c
Normal file
|
|
@ -0,0 +1,295 @@
|
||||||
|
#include "amduat/fed/replay.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
static int amduat_fed_octets_cmp(amduat_octets_t a, amduat_octets_t b) {
|
||||||
|
size_t min_len;
|
||||||
|
int cmp;
|
||||||
|
|
||||||
|
min_len = a.len < b.len ? a.len : b.len;
|
||||||
|
if (min_len > 0) {
|
||||||
|
cmp = memcmp(a.data, b.data, min_len);
|
||||||
|
if (cmp != 0) {
|
||||||
|
return cmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (a.len < b.len) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (a.len > b.len) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int amduat_fed_record_id_cmp(const amduat_fed_record_id_t *a,
|
||||||
|
const amduat_fed_record_id_t *b) {
|
||||||
|
if (a->type != b->type) {
|
||||||
|
return (a->type < b->type) ? -1 : 1;
|
||||||
|
}
|
||||||
|
if (a->ref.hash_id != b->ref.hash_id) {
|
||||||
|
return (a->ref.hash_id < b->ref.hash_id) ? -1 : 1;
|
||||||
|
}
|
||||||
|
return amduat_fed_octets_cmp(a->ref.digest, b->ref.digest);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int amduat_fed_record_cmp(const void *lhs, const void *rhs) {
|
||||||
|
const amduat_fed_record_t *a = (const amduat_fed_record_t *)lhs;
|
||||||
|
const amduat_fed_record_t *b = (const amduat_fed_record_t *)rhs;
|
||||||
|
|
||||||
|
if (a->logseq != b->logseq) {
|
||||||
|
return (a->logseq < b->logseq) ? -1 : 1;
|
||||||
|
}
|
||||||
|
return amduat_fed_record_id_cmp(&a->id, &b->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool amduat_fed_record_clone(const amduat_fed_record_t *src,
|
||||||
|
amduat_fed_record_t *out) {
|
||||||
|
if (src == NULL || out == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*out = *src;
|
||||||
|
if (!amduat_reference_clone(src->id.ref, &out->id.ref)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void amduat_fed_record_free(amduat_fed_record_t *record) {
|
||||||
|
if (record == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
amduat_reference_free(&record->id.ref);
|
||||||
|
memset(record, 0, sizeof(*record));
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool amduat_fed_bounds_ok(const amduat_fed_record_t *record,
|
||||||
|
uint64_t snapshot_id,
|
||||||
|
uint64_t log_prefix) {
|
||||||
|
if (record->snapshot_id < snapshot_id) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (record->snapshot_id > snapshot_id) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return record->logseq <= log_prefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool amduat_fed_ref_eq(amduat_reference_t a, amduat_reference_t b) {
|
||||||
|
return amduat_reference_eq(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool amduat_fed_tombstone_matches(const amduat_fed_record_t *tombstone,
|
||||||
|
const amduat_fed_record_t *record) {
|
||||||
|
if (tombstone->id.type != AMDUAT_FED_REC_TOMBSTONE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (record->id.type == AMDUAT_FED_REC_TOMBSTONE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return amduat_fed_ref_eq(tombstone->id.ref, record->id.ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool amduat_fed_tombstone_list_has(
|
||||||
|
const amduat_reference_t *refs,
|
||||||
|
size_t refs_len,
|
||||||
|
amduat_reference_t candidate) {
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < refs_len; ++i) {
|
||||||
|
if (amduat_fed_ref_eq(refs[i], candidate)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool amduat_fed_tombstone_list_push(amduat_reference_t **refs,
|
||||||
|
size_t *len,
|
||||||
|
size_t *cap,
|
||||||
|
amduat_reference_t ref) {
|
||||||
|
amduat_reference_t *next;
|
||||||
|
|
||||||
|
if (*len == *cap) {
|
||||||
|
size_t next_cap = (*cap == 0u) ? 4u : (*cap * 2u);
|
||||||
|
next = (amduat_reference_t *)realloc(*refs,
|
||||||
|
next_cap * sizeof(*next));
|
||||||
|
if (next == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*refs = next;
|
||||||
|
*cap = next_cap;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!amduat_reference_clone(ref, &(*refs)[*len])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
(*len)++;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool amduat_fed_record_validate(const amduat_fed_record_t *record) {
|
||||||
|
if (record == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (record->meta.visibility > 1u) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (record->meta.has_source > 1u) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (record->meta.has_source == 0u && record->meta.source_domain != 0u) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (record->id.type < AMDUAT_FED_REC_ARTIFACT ||
|
||||||
|
record->id.type > AMDUAT_FED_REC_TOMBSTONE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (record->id.ref.digest.len != 0u &&
|
||||||
|
record->id.ref.digest.data == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool amduat_fed_replay_domain(const amduat_fed_record_t *records,
|
||||||
|
size_t count,
|
||||||
|
uint32_t domain_id,
|
||||||
|
uint64_t snapshot_id,
|
||||||
|
uint64_t log_prefix,
|
||||||
|
amduat_fed_replay_view_t *out_view) {
|
||||||
|
amduat_fed_record_t *scratch;
|
||||||
|
amduat_reference_t *tombstones;
|
||||||
|
size_t tombstones_len;
|
||||||
|
size_t tombstones_cap;
|
||||||
|
size_t scratch_len;
|
||||||
|
size_t i;
|
||||||
|
size_t out_len;
|
||||||
|
bool ok;
|
||||||
|
|
||||||
|
if (out_view == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
out_view->records = NULL;
|
||||||
|
out_view->len = 0;
|
||||||
|
|
||||||
|
if (records == NULL && count != 0u) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count == 0u) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
scratch = (amduat_fed_record_t *)calloc(count, sizeof(*scratch));
|
||||||
|
if (scratch == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
tombstones = NULL;
|
||||||
|
tombstones_len = 0;
|
||||||
|
tombstones_cap = 0;
|
||||||
|
|
||||||
|
scratch_len = 0;
|
||||||
|
for (i = 0; i < count; ++i) {
|
||||||
|
const amduat_fed_record_t *record = &records[i];
|
||||||
|
|
||||||
|
if (record->meta.domain_id != domain_id) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!amduat_fed_record_validate(record)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!amduat_fed_bounds_ok(record, snapshot_id, log_prefix)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!amduat_fed_record_clone(record, &scratch[scratch_len])) {
|
||||||
|
while (scratch_len > 0) {
|
||||||
|
scratch_len--;
|
||||||
|
amduat_fed_record_free(&scratch[scratch_len]);
|
||||||
|
}
|
||||||
|
free(scratch);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
scratch_len++;
|
||||||
|
}
|
||||||
|
|
||||||
|
qsort(scratch, scratch_len, sizeof(*scratch), amduat_fed_record_cmp);
|
||||||
|
|
||||||
|
ok = true;
|
||||||
|
out_len = 0;
|
||||||
|
for (i = 0; i < scratch_len; ++i) {
|
||||||
|
amduat_fed_record_t current = scratch[i];
|
||||||
|
size_t j;
|
||||||
|
bool tombstoned;
|
||||||
|
|
||||||
|
scratch[i].id.ref.digest.data = NULL;
|
||||||
|
scratch[i].id.ref.digest.len = 0u;
|
||||||
|
|
||||||
|
if (current.id.type == AMDUAT_FED_REC_TOMBSTONE) {
|
||||||
|
for (j = 0; j < out_len; ++j) {
|
||||||
|
if (amduat_fed_tombstone_matches(¤t, &scratch[j])) {
|
||||||
|
amduat_fed_record_free(&scratch[j]);
|
||||||
|
memmove(&scratch[j],
|
||||||
|
&scratch[j + 1],
|
||||||
|
(out_len - j - 1) * sizeof(*scratch));
|
||||||
|
out_len--;
|
||||||
|
j--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!amduat_fed_tombstone_list_push(&tombstones,
|
||||||
|
&tombstones_len,
|
||||||
|
&tombstones_cap,
|
||||||
|
current.id.ref)) {
|
||||||
|
amduat_fed_record_free(¤t);
|
||||||
|
ok = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
amduat_fed_record_free(¤t);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
tombstoned = amduat_fed_tombstone_list_has(tombstones,
|
||||||
|
tombstones_len,
|
||||||
|
current.id.ref);
|
||||||
|
if (tombstoned) {
|
||||||
|
amduat_fed_record_free(¤t);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
scratch[out_len++] = current;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < tombstones_len; ++i) {
|
||||||
|
amduat_reference_free(&tombstones[i]);
|
||||||
|
}
|
||||||
|
free(tombstones);
|
||||||
|
|
||||||
|
if (!ok) {
|
||||||
|
for (i = 0; i < scratch_len; ++i) {
|
||||||
|
amduat_fed_record_free(&scratch[i]);
|
||||||
|
}
|
||||||
|
free(scratch);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
out_view->records = scratch;
|
||||||
|
out_view->len = out_len;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void amduat_fed_replay_view_free(amduat_fed_replay_view_t *view) {
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
if (view == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (view->records != NULL) {
|
||||||
|
for (i = 0; i < view->len; ++i) {
|
||||||
|
amduat_fed_record_free(&view->records[i]);
|
||||||
|
}
|
||||||
|
free(view->records);
|
||||||
|
}
|
||||||
|
view->records = NULL;
|
||||||
|
view->len = 0;
|
||||||
|
}
|
||||||
81
tests/fed/test_fed_ingest.c
Normal file
81
tests/fed/test_fed_ingest.c
Normal file
|
|
@ -0,0 +1,81 @@
|
||||||
|
#include "amduat/fed/ingest.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
static amduat_reference_t make_ref(amduat_hash_id_t hash_id,
|
||||||
|
const uint8_t *bytes,
|
||||||
|
size_t len) {
|
||||||
|
return amduat_reference(hash_id, amduat_octets(bytes, len));
|
||||||
|
}
|
||||||
|
|
||||||
|
static amduat_fed_record_t make_record(uint32_t domain_id,
|
||||||
|
uint64_t logseq,
|
||||||
|
amduat_fed_record_type_t type,
|
||||||
|
amduat_reference_t ref) {
|
||||||
|
amduat_fed_record_t record;
|
||||||
|
|
||||||
|
memset(&record, 0, sizeof(record));
|
||||||
|
record.meta.domain_id = domain_id;
|
||||||
|
record.meta.visibility = 1;
|
||||||
|
record.meta.has_source = 0;
|
||||||
|
record.id.type = type;
|
||||||
|
record.id.ref = ref;
|
||||||
|
record.logseq = logseq;
|
||||||
|
record.snapshot_id = 1;
|
||||||
|
record.log_prefix = 10;
|
||||||
|
return record;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int test_invalid_record(void) {
|
||||||
|
uint8_t key[] = {0x01};
|
||||||
|
amduat_fed_record_t record;
|
||||||
|
size_t error_index = 0;
|
||||||
|
|
||||||
|
record = make_record(1, 1, AMDUAT_FED_REC_ARTIFACT,
|
||||||
|
make_ref(1, key, sizeof(key)));
|
||||||
|
record.meta.visibility = 2;
|
||||||
|
|
||||||
|
if (amduat_fed_ingest_validate(&record, 1, &error_index, NULL) !=
|
||||||
|
AMDUAT_FED_INGEST_ERR_INVALID) {
|
||||||
|
fprintf(stderr, "expected invalid error\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (error_index != 0) {
|
||||||
|
fprintf(stderr, "expected error index 0\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int test_conflicting_duplicate(void) {
|
||||||
|
uint8_t key[] = {0x02};
|
||||||
|
amduat_reference_t ref = make_ref(1, key, sizeof(key));
|
||||||
|
amduat_fed_record_t records[2];
|
||||||
|
size_t error_index = 0;
|
||||||
|
size_t conflict_index = 0;
|
||||||
|
|
||||||
|
records[0] = make_record(1, 1, AMDUAT_FED_REC_ARTIFACT, ref);
|
||||||
|
records[1] = make_record(1, 2, AMDUAT_FED_REC_ARTIFACT, ref);
|
||||||
|
|
||||||
|
if (amduat_fed_ingest_validate(records, 2, &error_index, &conflict_index) !=
|
||||||
|
AMDUAT_FED_INGEST_ERR_CONFLICT) {
|
||||||
|
fprintf(stderr, "expected conflict error\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (error_index != 1 || conflict_index != 0) {
|
||||||
|
fprintf(stderr, "unexpected conflict indices\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
if (test_invalid_record() != 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (test_conflicting_duplicate() != 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
93
tests/fed/test_fed_registry.c
Normal file
93
tests/fed/test_fed_registry.c
Normal file
|
|
@ -0,0 +1,93 @@
|
||||||
|
#include "amduat/fed/registry.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
static bool octets_equal(amduat_octets_t a, amduat_octets_t b) {
|
||||||
|
if (a.len != b.len) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (a.len == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return memcmp(a.data, b.data, a.len) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int test_round_trip(void) {
|
||||||
|
uint8_t policy_a[] = {0xde, 0xad, 0xbe, 0xef};
|
||||||
|
amduat_fed_domain_state_t states[2];
|
||||||
|
amduat_fed_registry_value_t value;
|
||||||
|
amduat_fed_registry_value_t decoded;
|
||||||
|
amduat_octets_t encoded;
|
||||||
|
int exit_code = 1;
|
||||||
|
|
||||||
|
memset(states, 0, sizeof(states));
|
||||||
|
amduat_fed_registry_value_init(&value, states, 2);
|
||||||
|
|
||||||
|
states[0].domain_id = 42;
|
||||||
|
states[0].snapshot_id = 10;
|
||||||
|
states[0].log_prefix = 8;
|
||||||
|
states[0].last_logseq = 7;
|
||||||
|
states[0].admitted = 1;
|
||||||
|
states[0].policy_ok = 1;
|
||||||
|
states[0].policy_hash_id = 1;
|
||||||
|
states[0].policy_hash = amduat_octets(policy_a, sizeof(policy_a));
|
||||||
|
|
||||||
|
states[1].domain_id = 7;
|
||||||
|
states[1].snapshot_id = 3;
|
||||||
|
states[1].log_prefix = 2;
|
||||||
|
states[1].last_logseq = 2;
|
||||||
|
states[1].admitted = 0;
|
||||||
|
states[1].policy_ok = 0;
|
||||||
|
states[1].policy_hash_id = 0;
|
||||||
|
states[1].policy_hash = amduat_octets(NULL, 0);
|
||||||
|
|
||||||
|
if (!amduat_fed_registry_value_insert(&value, states[0]) ||
|
||||||
|
!amduat_fed_registry_value_insert(&value, states[1])) {
|
||||||
|
fprintf(stderr, "insert failed\n");
|
||||||
|
return exit_code;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!amduat_fed_registry_encode(&value, &encoded)) {
|
||||||
|
fprintf(stderr, "encode failed\n");
|
||||||
|
return exit_code;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&decoded, 0, sizeof(decoded));
|
||||||
|
if (!amduat_fed_registry_decode(encoded, &decoded)) {
|
||||||
|
fprintf(stderr, "decode failed\n");
|
||||||
|
amduat_octets_free(&encoded);
|
||||||
|
return exit_code;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (decoded.len != 2) {
|
||||||
|
fprintf(stderr, "decoded length mismatch\n");
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (decoded.states[0].domain_id != 7 ||
|
||||||
|
decoded.states[1].domain_id != 42) {
|
||||||
|
fprintf(stderr, "decoded order mismatch\n");
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!octets_equal(decoded.states[1].policy_hash,
|
||||||
|
amduat_octets(policy_a, sizeof(policy_a)))) {
|
||||||
|
fprintf(stderr, "decoded policy hash mismatch\n");
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
exit_code = 0;
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
amduat_octets_free(&encoded);
|
||||||
|
amduat_fed_registry_value_free(&decoded);
|
||||||
|
return exit_code;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
if (test_round_trip() != 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
134
tests/fed/test_fed_replay.c
Normal file
134
tests/fed/test_fed_replay.c
Normal file
|
|
@ -0,0 +1,134 @@
|
||||||
|
#include "amduat/fed/replay.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
static amduat_reference_t make_ref(amduat_hash_id_t hash_id,
|
||||||
|
const uint8_t *bytes,
|
||||||
|
size_t len) {
|
||||||
|
return amduat_reference(hash_id, amduat_octets(bytes, len));
|
||||||
|
}
|
||||||
|
|
||||||
|
static amduat_fed_record_t make_record(uint32_t domain_id,
|
||||||
|
uint64_t logseq,
|
||||||
|
uint64_t snapshot_id,
|
||||||
|
uint64_t log_prefix,
|
||||||
|
amduat_fed_record_type_t type,
|
||||||
|
amduat_reference_t ref) {
|
||||||
|
amduat_fed_record_t record;
|
||||||
|
|
||||||
|
memset(&record, 0, sizeof(record));
|
||||||
|
record.meta.domain_id = domain_id;
|
||||||
|
record.meta.visibility = 1;
|
||||||
|
record.meta.has_source = 0;
|
||||||
|
record.id.type = type;
|
||||||
|
record.id.ref = ref;
|
||||||
|
record.logseq = logseq;
|
||||||
|
record.snapshot_id = snapshot_id;
|
||||||
|
record.log_prefix = log_prefix;
|
||||||
|
return record;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int test_ordering(void) {
|
||||||
|
uint8_t d0[] = {0x00};
|
||||||
|
uint8_t d1[] = {0x01};
|
||||||
|
uint8_t d2[] = {0x02};
|
||||||
|
amduat_fed_record_t records[3];
|
||||||
|
amduat_fed_replay_view_t view;
|
||||||
|
|
||||||
|
records[0] = make_record(1, 5, 1, 10, AMDUAT_FED_REC_ARTIFACT,
|
||||||
|
make_ref(1, d1, sizeof(d1)));
|
||||||
|
records[1] = make_record(1, 5, 1, 10, AMDUAT_FED_REC_ARTIFACT,
|
||||||
|
make_ref(1, d0, sizeof(d0)));
|
||||||
|
records[2] = make_record(1, 4, 1, 10, AMDUAT_FED_REC_ARTIFACT,
|
||||||
|
make_ref(1, d2, sizeof(d2)));
|
||||||
|
|
||||||
|
if (!amduat_fed_replay_domain(records, 3, 1, 1, 10, &view)) {
|
||||||
|
fprintf(stderr, "replay failed\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (view.len != 3) {
|
||||||
|
fprintf(stderr, "ordering length mismatch\n");
|
||||||
|
amduat_fed_replay_view_free(&view);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (view.records[0].logseq != 4 ||
|
||||||
|
view.records[1].id.ref.digest.data[0] != 0x00 ||
|
||||||
|
view.records[2].id.ref.digest.data[0] != 0x01) {
|
||||||
|
fprintf(stderr, "ordering mismatch\n");
|
||||||
|
amduat_fed_replay_view_free(&view);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
amduat_fed_replay_view_free(&view);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int test_tombstone_scope(void) {
|
||||||
|
uint8_t key[] = {0xaa};
|
||||||
|
amduat_reference_t ref = make_ref(1, key, sizeof(key));
|
||||||
|
amduat_fed_record_t records[3];
|
||||||
|
amduat_fed_replay_view_t view;
|
||||||
|
|
||||||
|
records[0] = make_record(1, 1, 1, 10, AMDUAT_FED_REC_ARTIFACT, ref);
|
||||||
|
records[1] = make_record(1, 2, 1, 10, AMDUAT_FED_REC_TOMBSTONE, ref);
|
||||||
|
records[2] = make_record(2, 1, 1, 10, AMDUAT_FED_REC_ARTIFACT, ref);
|
||||||
|
|
||||||
|
if (!amduat_fed_replay_domain(records, 3, 1, 1, 10, &view)) {
|
||||||
|
fprintf(stderr, "replay domain 1 failed\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (view.len != 0) {
|
||||||
|
fprintf(stderr, "tombstone scope mismatch (domain 1)\n");
|
||||||
|
amduat_fed_replay_view_free(&view);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
amduat_fed_replay_view_free(&view);
|
||||||
|
|
||||||
|
if (!amduat_fed_replay_domain(records, 3, 2, 1, 10, &view)) {
|
||||||
|
fprintf(stderr, "replay domain 2 failed\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (view.len != 1) {
|
||||||
|
fprintf(stderr, "tombstone scope mismatch (domain 2)\n");
|
||||||
|
amduat_fed_replay_view_free(&view);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
amduat_fed_replay_view_free(&view);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int test_bounds(void) {
|
||||||
|
uint8_t key[] = {0xbb};
|
||||||
|
amduat_reference_t ref = make_ref(1, key, sizeof(key));
|
||||||
|
amduat_fed_record_t records[2];
|
||||||
|
amduat_fed_replay_view_t view;
|
||||||
|
|
||||||
|
records[0] = make_record(3, 1, 2, 10, AMDUAT_FED_REC_ARTIFACT, ref);
|
||||||
|
records[1] = make_record(3, 5, 1, 4, AMDUAT_FED_REC_ARTIFACT, ref);
|
||||||
|
|
||||||
|
if (!amduat_fed_replay_domain(records, 2, 3, 1, 4, &view)) {
|
||||||
|
fprintf(stderr, "replay bounds failed\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (view.len != 0) {
|
||||||
|
fprintf(stderr, "bounds mismatch\n");
|
||||||
|
amduat_fed_replay_view_free(&view);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
amduat_fed_replay_view_free(&view);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
if (test_ordering() != 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (test_tombstone_scope() != 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (test_bounds() != 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
@ -162,3 +162,6 @@ ASL/DOMAIN-MODEL/1 does not define:
|
||||||
* Encoding formats
|
* Encoding formats
|
||||||
* Storage layouts or filesystem assumptions
|
* Storage layouts or filesystem assumptions
|
||||||
* Governance workflows beyond admission and policy compatibility
|
* Governance workflows beyond admission and policy compatibility
|
||||||
|
|
||||||
|
Middle layer (informative): the daemon/service boundary around core logic that
|
||||||
|
owns network transport, admission workflows, and operational policy.
|
||||||
|
|
|
||||||
|
|
@ -179,6 +179,9 @@ Federation MUST NOT bypass ASL/LOG/1 ordering or ASL/1-CORE-INDEX semantics.
|
||||||
* Witness signatures
|
* Witness signatures
|
||||||
* Domain admission and trust policy
|
* Domain admission and trust policy
|
||||||
|
|
||||||
|
Middle layer (informative): the daemon/service boundary around core logic that
|
||||||
|
owns network transport, admission workflows, and operational policy.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 12. Summary
|
## 12. Summary
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,9 @@ Non-goals:
|
||||||
* New execution operators
|
* New execution operators
|
||||||
* Domain policy or governance rules
|
* Domain policy or governance rules
|
||||||
|
|
||||||
|
Middle layer (informative): the daemon/service boundary around core logic that
|
||||||
|
owns network transport, admission workflows, and operational policy.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 2. Core Objects (Unified View)
|
## 2. Core Objects (Unified View)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue