Infinite-state systems such as distributed protocols are challenging to verify using interactive theorem provers or automatic verification tools. Of these techniques, deductive verification is highly expressive but requires the user to annotate the system with inductive invariants. To relieve the user from this laborintensive and challenging task, invariant inference aims to find inductive invariants automatically. Unfortunately, when applied to infinite-state systems such as distributed protocols, existing inference techniques often diverge, which limits their applicability. This paper proposes user-guided invariant inference based on phase invariants, which capture the different logical phases of the protocol. Users conveys their intuition by specifying a phase structure, an automaton with edges labeled by program transitions; the tool automatically infers assertions that hold in the automaton's states, resulting in a full safety proof. The additional structure from phases guides the inference procedure towards finding an invariant. Our results show that user guidance by phase structures facilitates successful inference beyond the state of the art. We find that phase structures are pleasantly well matched to the intuitive reasoning routinely used by domain experts to understand why distributed protocols are correct, so that providing a phase structure reuses this existing intuition. 1 type key 2 type value 3 type node 4 type sequnum 5 6 relation owner: node, key 7 relation table: node, key, value 8 relation transfer_msg: node, node, 9 key, value, seqnum 10 relation ack_msg: node, node, seqnum 11 relation seqnum_sent: node, seqnum 12 relation unacked: node, node, 13 key, value, seqnum 14 relation seqnum_recvd: node, node, seqnum 15 16 init ∀n1, n2, k. owner(n1,k)∧owner(n2,k) 17 → n1 = n2 18 init // all other relations are empty 19 20 action reshard(n_old:node, n_new:node, 21 k:key, value:sequnum) 22 require table(n_old, k, v)23 ∧¬seqnum_sent(n_old, s) 24 seqnum_sent(n_old, s) := true 25 table(n_old, k, v) := false 26 owner(n_old, k) := false 27 transfer_msg(n_old, n_new, k, v, s) := true 28 unacked(n_old, n_new, k, v, s) := true 29 30 action drop_transfer_msg(src:node, dst:node, 31 k:key, v:value, s:seqnum) 32 require transfer_msg(src, dst, k, v, s) 33 transfer_msg(src, dst, k, v, s) := false 34 35 action retransmit(src:node, dst:node, 36 k:key, v:value, s:seqnum) 37 require unacked(src, dst, k, v, s) 38 transfer_msg(src, dst, k, v, s) := true 39 action recv_transfer_msg(src:node, n:node, 40 k:key, v:value, s:seqnum) 41 require transfer_msg(src, n, k, v, s) 42 ∧¬seqnum_recvd(n, src, s) 43 seqnum_recvd(n, src, s) := true 44 table(n, k, v) := true 45 owner(n, k) := true 46 47 action send_ack(src:node, n:node, 48