Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions include/lantern/http/server.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ struct lantern_http_server_callbacks {
int (*set_validator_status)(void *context, uint64_t global_index, bool enabled);
int (*metrics_snapshot)(void *context, struct lantern_metrics_snapshot *out_snapshot);
int (*finalized_state_ssz)(void *context, uint8_t **out_bytes, size_t *out_len);
int (*finalized_block_ssz)(void *context, uint8_t **out_bytes, size_t *out_len);
int (*get_is_aggregator)(void *context, bool *out_enabled);
int (*set_is_aggregator)(void *context, bool enabled, bool *out_previous);
};
Expand Down
5 changes: 5 additions & 0 deletions include/lantern/storage/storage.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@ int lantern_storage_load_state_bytes_for_root(
const LanternRoot *root,
uint8_t **out_data,
size_t *out_len);
int lantern_storage_load_block_bytes_for_root(
const char *data_dir,
const LanternRoot *root,
uint8_t **out_data,
size_t *out_len);
int lantern_storage_prune_before_slot(
const char *data_dir,
uint64_t slot,
Expand Down
173 changes: 26 additions & 147 deletions src/core/client.c
Original file line number Diff line number Diff line change
Expand Up @@ -2354,19 +2354,19 @@ static int checkpoint_sync_extract_http_body(
return rc;
}

static int checkpoint_sync_fetch_state_bytes(
static int checkpoint_sync_fetch_bytes(
const char *checkpoint_sync_url,
uint8_t **out_state_bytes,
size_t *out_state_len,
uint8_t **out_bytes,
size_t *out_len,
int *out_status_code)
{
if (!checkpoint_sync_url || !out_state_bytes || !out_state_len || !out_status_code)
if (!checkpoint_sync_url || !out_bytes || !out_len || !out_status_code)
{
return -1;
}

*out_state_bytes = NULL;
*out_state_len = 0;
*out_bytes = NULL;
*out_len = 0;
*out_status_code = 0;

char *host = NULL;
Expand Down Expand Up @@ -2485,8 +2485,8 @@ static int checkpoint_sync_fetch_state_bytes(
response,
response_len,
out_status_code,
out_state_bytes,
out_state_len);
out_bytes,
out_len);

cleanup:
#if !defined(_WIN32)
Expand Down Expand Up @@ -2519,7 +2519,7 @@ static lantern_client_error client_load_state_from_checkpoint(
uint8_t *state_bytes = NULL;
size_t state_len = 0;
int status_code = 0;
int fetch_rc = checkpoint_sync_fetch_state_bytes(
int fetch_rc = checkpoint_sync_fetch_bytes(
checkpoint_sync_url,
&state_bytes,
&state_len,
Expand Down Expand Up @@ -2558,6 +2558,9 @@ static lantern_client_error client_load_state_from_checkpoint(

LanternState decoded;
lantern_state_init(&decoded);
LanternBlock anchor_block;
memset(&anchor_block, 0, sizeof(anchor_block));
bool anchor_block_initialized = false;
bool decoded_owned = true;
lantern_client_error result = LANTERN_CLIENT_OK;

Expand Down Expand Up @@ -2640,157 +2643,28 @@ static lantern_client_error client_load_state_from_checkpoint(
goto cleanup;
}

LanternCheckpoint original_latest_justified = decoded.latest_justified;
LanternCheckpoint original_latest_finalized = decoded.latest_finalized;
LanternBlockHeader anchor_header = decoded.latest_block_header;
anchor_header.state_root = state_root;
anchor_block.slot = decoded.latest_block_header.slot;
anchor_block.proposer_index = decoded.latest_block_header.proposer_index;
anchor_block.parent_root = decoded.latest_block_header.parent_root;
anchor_block.state_root = state_root;
lantern_block_body_init(&anchor_block.body);
anchor_block_initialized = true;

LanternRoot anchor_root;
if (lantern_hash_tree_root_block_header(&anchor_header, &anchor_root) != SSZ_SUCCESS)
if (lantern_hash_tree_root_block(&anchor_block, &anchor_root) != SSZ_SUCCESS)
{
lantern_log_error(
"checkpoint_sync",
&meta,
"failed to compute checkpoint anchor root");
"failed to compute checkpoint anchor block root");
result = LANTERN_CLIENT_ERR_GENESIS;
goto cleanup;
}

char header_parent_hex[(LANTERN_ROOT_SIZE * 2u) + 3u];
char header_state_root_hex[(LANTERN_ROOT_SIZE * 2u) + 3u];
char original_justified_root_hex[(LANTERN_ROOT_SIZE * 2u) + 3u];
char original_finalized_root_hex[(LANTERN_ROOT_SIZE * 2u) + 3u];
format_root_hex(
&decoded.latest_block_header.parent_root,
header_parent_hex,
sizeof(header_parent_hex));
format_root_hex(
&decoded.latest_block_header.state_root,
header_state_root_hex,
sizeof(header_state_root_hex));
format_root_hex(
&original_latest_justified.root,
original_justified_root_hex,
sizeof(original_justified_root_hex));
format_root_hex(
&original_latest_finalized.root,
original_finalized_root_hex,
sizeof(original_finalized_root_hex));

lantern_log_info(
"checkpoint_sync",
&meta,
"decoded checkpoint state slot=%" PRIu64
" header_slot=%" PRIu64 " proposer=%" PRIu64 " header_parent=%s"
" header_state_root=%s justified_slot=%" PRIu64 " justified_root=%s"
" finalized_slot=%" PRIu64 " finalized_root=%s",
decoded.slot,
decoded.latest_block_header.slot,
decoded.latest_block_header.proposer_index,
header_parent_hex[0] ? header_parent_hex : "0x0",
header_state_root_hex[0] ? header_state_root_hex : "0x0",
original_latest_justified.slot,
original_justified_root_hex[0] ? original_justified_root_hex : "0x0",
original_latest_finalized.slot,
original_finalized_root_hex[0] ? original_finalized_root_hex : "0x0");

/*
* Keep the fetched checkpoint state canonical. Fork choice rewrites the
* checkpoint roots to the synthetic anchor locally during bootstrap, but
* mutating the decoded state here changes its hash and causes a different
* anchor to be recomputed later from the modified snapshot.
*
* We still materialize the anchor-alias view below for diagnostics so the
* logs show how the state/root pair would drift if those checkpoint roots
* were rewritten before fork-choice initialization.
*/
LanternState anchor_checkpoint_alias = decoded;
anchor_checkpoint_alias.latest_justified.root = anchor_root;
anchor_checkpoint_alias.latest_finalized.root = anchor_root;

char state_root_hex[(LANTERN_ROOT_SIZE * 2u) + 3u];
char anchor_root_hex[(LANTERN_ROOT_SIZE * 2u) + 3u];
LanternRoot adjusted_state_root = {0};
LanternRoot adjusted_anchor_root = {0};
bool have_adjusted_state_root = false;
bool have_adjusted_anchor_root = false;
if (lantern_hash_tree_root_state(&anchor_checkpoint_alias, &adjusted_state_root) == SSZ_SUCCESS)
{
have_adjusted_state_root = true;
LanternBlockHeader adjusted_anchor_header = anchor_checkpoint_alias.latest_block_header;
adjusted_anchor_header.state_root = adjusted_state_root;
if (lantern_hash_tree_root_block_header(
&adjusted_anchor_header,
&adjusted_anchor_root)
== SSZ_SUCCESS)
{
have_adjusted_anchor_root = true;
}
else
{
lantern_log_warn(
"checkpoint_sync",
&meta,
"failed to compute adjusted checkpoint anchor root after checkpoint root override");
}
}
else
{
lantern_log_warn(
"checkpoint_sync",
&meta,
"failed to compute adjusted checkpoint state root after checkpoint root override");
}

char adjusted_state_root_hex[(LANTERN_ROOT_SIZE * 2u) + 3u];
char adjusted_anchor_root_hex[(LANTERN_ROOT_SIZE * 2u) + 3u];
char adjusted_justified_root_hex[(LANTERN_ROOT_SIZE * 2u) + 3u];
char adjusted_finalized_root_hex[(LANTERN_ROOT_SIZE * 2u) + 3u];
format_root_hex(&state_root, state_root_hex, sizeof(state_root_hex));
format_root_hex(&anchor_root, anchor_root_hex, sizeof(anchor_root_hex));
format_root_hex(
&adjusted_state_root,
adjusted_state_root_hex,
sizeof(adjusted_state_root_hex));
format_root_hex(
&adjusted_anchor_root,
adjusted_anchor_root_hex,
sizeof(adjusted_anchor_root_hex));
format_root_hex(
&anchor_checkpoint_alias.latest_justified.root,
adjusted_justified_root_hex,
sizeof(adjusted_justified_root_hex));
format_root_hex(
&anchor_checkpoint_alias.latest_finalized.root,
adjusted_finalized_root_hex,
sizeof(adjusted_finalized_root_hex));

lantern_log_info(
"checkpoint_sync",
&meta,
"checkpoint root override justified_before=%s justified_after=%s"
" finalized_before=%s finalized_after=%s original_state_root=%s"
" adjusted_state_root=%s original_anchor_root=%s adjusted_anchor_root=%s"
" adjusted_anchor_matches_original=%s",
original_justified_root_hex[0] ? original_justified_root_hex : "0x0",
adjusted_justified_root_hex[0] ? adjusted_justified_root_hex : "0x0",
original_finalized_root_hex[0] ? original_finalized_root_hex : "0x0",
adjusted_finalized_root_hex[0] ? adjusted_finalized_root_hex : "0x0",
state_root_hex[0] ? state_root_hex : "0x0",
have_adjusted_state_root
? (adjusted_state_root_hex[0] ? adjusted_state_root_hex : "0x0")
: "<unavailable>",
anchor_root_hex[0] ? anchor_root_hex : "0x0",
have_adjusted_anchor_root
? (adjusted_anchor_root_hex[0] ? adjusted_anchor_root_hex : "0x0")
: "<unavailable>",
(have_adjusted_anchor_root
&& memcmp(
adjusted_anchor_root.bytes,
anchor_root.bytes,
LANTERN_ROOT_SIZE)
== 0)
? "true"
: "false");

lantern_state_reset(&client->state);
client->state = decoded;
Expand All @@ -2812,6 +2686,10 @@ static lantern_client_error client_load_state_from_checkpoint(

cleanup:
free(state_bytes);
if (anchor_block_initialized)
{
lantern_block_body_reset(&anchor_block.body);
}
if (decoded_owned)
{
lantern_state_reset(&decoded);
Expand Down Expand Up @@ -4004,6 +3882,7 @@ static lantern_client_error client_start_apis(struct lantern_client *client)
http_config.callbacks.set_validator_status = http_set_validator_status_cb;
http_config.callbacks.metrics_snapshot = metrics_snapshot_cb;
http_config.callbacks.finalized_state_ssz = http_finalized_state_ssz_cb;
http_config.callbacks.finalized_block_ssz = http_finalized_block_ssz_cb;
http_config.callbacks.get_is_aggregator = http_get_is_aggregator_cb;
http_config.callbacks.set_is_aggregator = http_set_is_aggregator_cb;
if (client->http_port != 0)
Expand Down
64 changes: 64 additions & 0 deletions src/core/client_http.c
Original file line number Diff line number Diff line change
Expand Up @@ -680,3 +680,67 @@ int http_finalized_state_ssz_cb(void *context, uint8_t **out_bytes, size_t *out_
}
return LANTERN_HTTP_CB_ERR_IO;
}

/**
* Get finalized signed block SSZ bytes for the finalized block endpoint.
*
* @param context Client instance
* @param out_bytes Output buffer pointer (caller owns and must free)
* @param out_len Output byte length
*
* @return 0 on success
* @return LANTERN_HTTP_CB_ERR_INVALID_PARAM if inputs are NULL
* @return LANTERN_HTTP_CB_ERR_INVALID_STATE if client has no snapshot or data dir
* @return LANTERN_HTTP_CB_ERR_NOT_FOUND if finalized block is unavailable
* @return LANTERN_HTTP_CB_ERR_IO on storage read failure
*/
int http_finalized_block_ssz_cb(void *context, uint8_t **out_bytes, size_t *out_len)
{
if (!context || !out_bytes || !out_len)
{
return LANTERN_HTTP_CB_ERR_INVALID_PARAM;
}

*out_bytes = NULL;
*out_len = 0;

struct lantern_client *client = context;
if (!client->data_dir)
{
return LANTERN_HTTP_CB_ERR_INVALID_STATE;
}

if (!client->has_fork_choice)
{
return LANTERN_HTTP_CB_ERR_INVALID_STATE;
}

LanternCheckpoint finalized;
if (!lantern_fork_choice_read_checkpoint_snapshot(
&client->fork_choice,
NULL,
&finalized))
{
return LANTERN_HTTP_CB_ERR_INVALID_STATE;
}

if (lantern_root_is_zero(&finalized.root))
{
return LANTERN_HTTP_CB_ERR_NOT_FOUND;
}

int load_rc = lantern_storage_load_block_bytes_for_root(
client->data_dir,
&finalized.root,
out_bytes,
out_len);
if (load_rc == 0)
{
return LANTERN_HTTP_CB_OK;
}
if (load_rc > 0)
{
return LANTERN_HTTP_CB_ERR_NOT_FOUND;
}
return LANTERN_HTTP_CB_ERR_IO;
}
12 changes: 12 additions & 0 deletions src/core/client_services_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,18 @@ int metrics_snapshot_cb(void *context, struct lantern_metrics_snapshot *out_snap
*/
int http_finalized_state_ssz_cb(void *context, uint8_t **out_bytes, size_t *out_len);

/**
* Get finalized signed block SSZ bytes for checkpoint sync.
*
* @param context Client instance
* @param out_bytes Output buffer pointer (caller owns and must free)
* @param out_len Output byte length
* @return 0 on success, negative on failure
*
* @note Thread safety: Reads fork-choice's checkpoint snapshot.
*/
int http_finalized_block_ssz_cb(void *context, uint8_t **out_bytes, size_t *out_len);


/* ============================================================================
* Reqresp Callback Functions
Expand Down
Loading
Loading