-
Notifications
You must be signed in to change notification settings - Fork 11.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
RFC: Client State Persistence & Reloads (DO NOT MERGE) #270
Conversation
@gdanezis I would love your input on this |
Hey, here are some thoughts:
This
I think if you have
Why not simply keeping a structure
I think the approach you outline to avoid a client double spending an object by mistake is broadly sound. I would have a structure more like:
Then upon confirmation of an order (when the cert has been given to 2f+1 authorities) I would remove the Order from |
Thanks @gdanezis , here's my comments in-line.
I'm not sure of the full intent myself. @patrickkuo why was this added?
Agreed but the problem is there can be a lag between when we receive a ref and when we fetch the actual object, since objects can be arbitrarily sized. This
If this is indeed low cost in RocksDB, then it makes sense to use.
Why do I need two data structures here? |
object_certs allows us to retrieve certificates chains of an object id. this mapping is needed because each certificate can have multiple output objects which shares the same cert, and we don't store the output of a tx anywhere else. |
Related to this: Could we add detailed doc comment to each field of |
```rust | ||
pub struct ClientState<AuthorityClient> { | ||
/// Our FastPay address. | ||
address: FastPayAddress, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think a client needs to manage multiple addresses. One way to represent this might be something like:
struct AddressState {
pending_transactions: BTreeMap<ObjectID, Order>,
certificates: BTreeMap<TransactionDigest, CertifiedOrder>,
object_sequence_numbers: BTreeMap<ObjectID, SequenceNumber>,
object_refs: BTreeMap<ObjectID, ObjectRef>,
object_certs: BTreeMap<ObjectID, Vec<TransactionDigest>>,
}
and
struct ClientState {
addresses: BTreeMap<FastPayAddress, AddressState>,
// ... committee info, which is the same for all addresses
}
```
/// Our FastPay address. | ||
address: FastPayAddress, | ||
/// Our signature key. | ||
secret: KeyPair, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The client state should know the public key for each of its addresses, but not the private key.
THis aligns the behavior of our Dag with that of the store - adds the genesis by default to the Dag as part of the constructor, - unit tests that initial provisioning, - adapts the behavior of tests to not feed genesis manually. Fixes #270
THis aligns the behavior of our Dag with that of the store - adds the genesis by default to the Dag as part of the constructor, - unit tests that initial provisioning, - adapts the behavior of tests to not feed genesis manually. Fixes MystenLabs#270
Persisting client state
TLDR:
1. How do we take advantage of storage to aid proper client reloads?
2. Do we persist all old objects?
3. We should persist all certificates, even for objects we no longer own, right?
Current client state variables
The following data structures from https://github.com/MystenLabs/fastnft/blob/main/fastpay_core/src/client.rs#L59 are used by the client but to support storage, they'll be modified and represented in some
store: Arc<ClientStore>,
similar on AuthorityStore https://github.com/MystenLabs/fastnft/blob/main/fastpay_core/src/authority/authority_store.rsHowever with some changes
The following will be kept and persisted normally (as DBMap):
The following will be deleted:
object_sequence_numbers: BTreeMap<ObjectID, SequenceNumber>
as it can be derived fromobject_refs: BTreeMap<ObjectID, ObjectRef>
The following will be added:
past_objects_map: DBMap<ObjectID, BTreeMap<SequenceNumber, Object>>
which stores all versions of the old objects (similar object entries will be compacted)latest_object_map: DBMap<ObjectID, Object>
which stores the latest object (for lightweight retrieval)The following will be changed
pending_transfer: Option<Order>
should be changed topending_orders: HashMap<ObjectID, Order>
and should be persisted to prevent equivocation on crash & restartWhat's the proper way of saving and reloading pending orders?
My thought process:
Upon every tx order, the client saves the ObjectId-Order pair to the
pending_transfer
map and clears it when finalized. While a flag is set for an object, the client cannot mutate the object. If the client crashes before clearing the flag, it can replay the order after restarting. This is safe due to idempotence.What's wrong with this approach?