Skip to content

Conversation

cjjdespres
Copy link
Member

@cjjdespres cjjdespres commented Sep 17, 2025

If a daemon is stopped at certain points while syncing a converting ledger to a network, the underlying databases can be left in an unexpected state: the num_accounts in the database will be set to a high value (because the daemon added a batch of accounts at high addresses) but not all accounts at lower indices will be present (because the daemon didn't get to filling them in yet). This will cause functions like iteri and get_at_index_exn to throw unexpected exceptions when they get to addresses that are missing accounts.

A couple of new ledger functions have been added to account for this possibility, which are used in an updated ledger database sync check implementation. The sync check ensure that for any index under num_accounts, either both databases are missing an account at that index, or both databases have the same account at that index up to account conversion.

A few related unit tests have been added to the converting merkle tree, one of which does not pass without these changes being in place.

@cjjdespres
Copy link
Member Author

!ci-build-me

@cjjdespres cjjdespres force-pushed the cjjdespres/make-sync-check-more-defensive branch from b6d20f9 to c59c699 Compare September 17, 2025 20:39
@cjjdespres
Copy link
Member Author

!ci-build-me

Copy link
Member

@glyh glyh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved, yet please consider improve readability of iteri_untrusted

get mdb (Location.Account addr) |> Option.value_exn
get mdb (Location.Account addr)

let get_at_index_exn mdb index = get_at_index mdb index |> Option.value_exn
Copy link
Member

@glyh glyh Sep 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: add ~message and ~here for Option.value_exn please? ~here should be propagated from outside.

Ok (`Existed, location)

let iteri_untrusted t ~f =
match Account_location.last_location_address t with
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: use Option.iter here

get t (Location.Account addr) |> Option.value_exn
get t (Location.Account addr)

let get_at_index_exn t index =
Copy link
Member

@glyh glyh Sep 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: add ~message and ~here for Option.value_exn please? ~here should be propagated from outside.

assert_is_attached t ;
let num_accounts = num_accounts t in
Sequence.range ~stop:`exclusive 0 num_accounts
|> Sequence.iter ~f:(fun i -> f i (get_at_index t i))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we do a simple for loop here? It seems more readable than this. I'll have to check the API of sequence to understand if this is written correctly.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let range ?(stride = 1) ?(start = `inclusive) ?(stop = `exclusive) start_v stop_v =
  let step =
    match stop with
    | `inclusive when stride >= 0 ->
      fun i -> if i > stop_v then Done else Yield { value = i; state = i + stride }
    | `inclusive ->
      fun i -> if i < stop_v then Done else Yield { value = i; state = i + stride }
    | `exclusive when stride >= 0 ->
      fun i -> if i >= stop_v then Done else Yield { value = i; state = i + stride }
    | `exclusive ->
      fun i -> if i <= stop_v then Done else Yield { value = i; state = i + stride }
  in
  let init =
    match start with
    | `inclusive -> start_v
    | `exclusive -> start_v + stride
  in
  unfold_step ~init ~f:step
;;

it seems stop being exclusive is the default. Thought I'm looking at Base v0.17.2

@cjjdespres
Copy link
Member Author

!ci-build-me

@cjjdespres
Copy link
Member Author

I've now implemented iteri in terms of iteri_untrusted, so it should be clearer that the Sequence code was there before.

I agree with your ~here comments, but could that be done separately? I'll open an issue so we don't lose track of it. I'd rather do a pass for all of these _exn functions.

@glyh
Copy link
Member

glyh commented Sep 18, 2025

Okay, could we at least add ~message to Option.value_exn. This is hard to debug as sometimes there's no stack trace at all with these unwrapping

If a daemon is stopped at certain points while syncing a converting
ledger to a network, the underlying databases can be left in an
unexpected state: the num_accounts in the database will be set to a high
value (because the daemon added a batch of accounts at high addresses)
but not all accounts at lower indices will be present (because the
daemon didn't get to filling them in yet). This will cause functions
like iteri and get_at_index_exn to throw unexpected exceptions when they
get to addresses that are missing accounts.

A couple of new ledger functions have been added to account for this
possibility, which are used in an updated ledger database sync check
implementation. A few related unit tests have been added to the
converting merkle tree, one of which does not pass without these changes
being in place.
The ~here is set to the point of usage of Option.value_exn itself to
conform with our code base's current practices, but we might want to
have it passed in from the caller of these functions.
@cjjdespres cjjdespres force-pushed the cjjdespres/make-sync-check-more-defensive branch from 669e742 to 688e286 Compare September 30, 2025 16:05
@cjjdespres
Copy link
Member Author

I added the one commit to add a ~message and ~here (not passed in from the caller yet), then rebased onto latest compatible.

@cjjdespres
Copy link
Member Author

!ci-build-me

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: No status
Development

Successfully merging this pull request may close these issues.

2 participants