Skip to content
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

Fix certificate update issue which cause subsequence object transfer fail #170

Merged
merged 7 commits into from
Jan 18, 2022

Conversation

patrickkuo
Copy link
Contributor

@patrickkuo patrickkuo commented Jan 13, 2022

@patrickkuo patrickkuo force-pushed the pat/client-side-cert branch 2 times, most recently from 7a932f1 to 89ad047 Compare January 14, 2022 10:44
@patrickkuo patrickkuo force-pushed the pat/client-side-cert branch 2 times, most recently from 8d13173 to 84f38ba Compare January 16, 2022 17:34
@patrickkuo patrickkuo marked this pull request as ready for review January 16, 2022 17:50
@patrickkuo
Copy link
Contributor Author

This PR is ready for review, I will add more client side test for move calls in my next PR.

Copy link
Collaborator

@gdanezis gdanezis left a comment

Choose a reason for hiding this comment

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

This is good.

I only have one architectural issue here: the client seems to not remember which certificates are "pending". These are certificates that the client knows BUT have not been confirmed by 2f+1 authorities. We should probably remember these, and re-submit them on restart of the client, or retry to send them if the network goes down. Most importantly the client must NEVER reuse the sequence number used in an order that has a signature from an authority.

@@ -235,12 +232,12 @@ impl AccountsConfig {
if let Address::FastPay(recipient) = &transfer.recipient {
if let Some(config) = self.accounts.get_mut(recipient) {
if let Err(position) = config
.received_certificates
.certificates
.binary_search_by_key(&certificate.order.digest(), |cert| {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why not store the certificates in a HashMap/Btree sorted by digest instead?

@patrickkuo
Copy link
Contributor Author

patrickkuo commented Jan 18, 2022

The client query the authorities and confirm all the missing certificates at the beginning of communicate_transfers https://github.com/MystenLabs/fastnft/blob/178a9fb289db3222fb4bf51208501513f13d41b5/fastpay_core/src/client.rs#L448
I think this is sufficient to prevent "pending" certificates?

Copy link
Contributor

@huitseeker huitseeker left a comment

Choose a reason for hiding this comment

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

Mostly nits, one comment on update_certificates

Comment on lines 311 to 315
let requested_certificate = match request.request_sequence_number {
Some(seq) => {
// TODO(https://github.com/MystenLabs/fastnft/issues/123): Here we need to develop a strategy
// to provide back to the client the object digest for specific objects requested. Probably,
// we have to return the full ObjectRef and why not the actual full object here.

// Get the Transaction Digest that created the object
let parent_iterator = self
.get_parent_iterator(request.object_id, Some(seq.increment()?))
.await?;
let (_, transaction_digest) = parent_iterator
.first()
.ok_or(FastPayError::CertificateNotfound)?;
// Get the cert from the transaction digest
Some(
self.read_certificate(transaction_digest)
.await?
.ok_or(FastPayError::CertificateNotfound)?,
)
}
None => None,
};
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: you're only doing something non-trivial with one arm of the match, so that's a good place to reformulate your :

let rc = match foo {
    Some(bar) => {
    ...
        Some(baz)
    }
    None => None
}

Into:

let rc = {
    if let Some(bar) = foo {
        ...
        Some(baz)
    } else {
        None
    }
}

Or even

let rc = foo.map(|baz| {
    ...
    baz
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

my first attempt was .map, but couldn't get it working because of async and ?, are there any way to make it work?

Copy link
Contributor

Choose a reason for hiding this comment

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

I was just mentioning it for completeness. It's actually hard, though not impossible, to work with a map in contexts where you're like to short-circuit with a ?, you have to keep things as a Result, thread operations through flat_map/map, extirpate your Result out of whatever you're returning, and only ? or return at the end.

Sometimes the if let construct is just simpler.

Comment on lines 512 to 513
.filter(|(id, _, _)| id == &object_id)
.map(|(_, seq, _)| seq)
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: consider

Suggested change
.filter(|(id, _, _)| id == &object_id)
.map(|(_, seq, _)| seq)
.filter_map(|(id, seq, _)| if id == &object_id { Some(seq) } else { None })

?

.find(|(id, _, _)| object_id == id)
.copied()
.ok_or::<FastPayError>(FastPayError::ObjectNotFound)
.unwrap();
Copy link
Contributor

Choose a reason for hiding this comment

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

Are you looking for a ? instead of the panicking .unwrap()?

@@ -920,15 +935,15 @@ where
let transfer = Transfer {
object_ref: (
object_id,
self.next_sequence_number(object_id),
self.next_sequence_number(&object_id).unwrap(),
Copy link
Contributor

Choose a reason for hiding this comment

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

You're returning a Result here, is the panic appropriate?

// TODO(https://github.com/MystenLabs/fastnft/issues/123): Include actual object digest here
ObjectDigest::new([0; 32]),
),
sender: self.address,
recipient: Address::FastPay(recipient),
gas_payment: (
gas_payment,
self.next_sequence_number(gas_payment),
self.next_sequence_number(&gas_payment).unwrap(),
Copy link
Contributor

Choose a reason for hiding this comment

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

Same Q: You're returning a Result here, is the panic appropriate?

),
)
.await?;
}
Ok(self.sent_certificates.last().unwrap().clone())
Ok(self.certificates(order.object_id()).last().unwrap().clone())
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: A comment on the safety of last().unwrap() somewhere in this function, one that would refer to update_certificates only growing the set, should help the next implementer reason locally.

@patrickkuo patrickkuo force-pushed the pat/client-side-cert branch from 9d9c2fe to 01094ce Compare January 18, 2022 17:57
Copy link
Contributor

@huitseeker huitseeker left a comment

Choose a reason for hiding this comment

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

thanks!

@patrickkuo patrickkuo merged commit 5a946cd into main Jan 18, 2022
@patrickkuo patrickkuo deleted the pat/client-side-cert branch January 24, 2022 00:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants