-
Notifications
You must be signed in to change notification settings - Fork 167
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
Generalize Vote and SignatureAggregator over type of value they hold. #2887
Conversation
@@ -125,13 +125,13 @@ pub struct ChainManager { | |||
pub timeout: Option<TimeoutCertificate>, | |||
/// Latest vote we have cast, to validate or confirm. | |||
#[debug(skip_if = Option::is_none)] | |||
pub pending: Option<Vote>, | |||
pub pending: Option<Vote<CertificateValue>>, |
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.
This will be tricky to get rid of as we set all three types of CertificateValue
here.
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 timeout_vote
is already separate. We could split up the other two as well.
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.
Agreed to do in a seperate PR
linera-chain/src/block.rs
Outdated
@@ -109,3 +112,21 @@ impl From<Timeout> for HashedCertificateValue { | |||
HashedCertificateValue::new_timeout(value.chain_id, value.height, value.epoch) | |||
} | |||
} | |||
|
|||
impl From<HashedCertificateValue> for Hashed<Timeout> { | |||
fn from(hv: HashedCertificateValue) -> Hashed<Timeout> { |
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.
nit: (See naming guidelines) We don't use acronyms so it should be value: HashedCertificateValue
or (hashed_)certificate_value: HashedCertificateValue
.
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.
Done in Replace From with TryFrom
linera-chain/src/block.rs
Outdated
let hash = hv.hash(); | ||
match hv.into_inner() { | ||
CertificateValue::Timeout(timeout) => Hashed::unchecked_new(timeout, hash), | ||
_ => panic!("Expected a Timeout value"), |
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 would feel more confident if we used TryFrom
instead of From
, and then expect
ed or unwrap
ped at the call site if we're sure.
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'll change this to TryFrom
and make a separate PR where I'll make a sweep over other ones
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.
Done in Replace From with TryFrom
@@ -125,13 +125,13 @@ pub struct ChainManager { | |||
pub timeout: Option<TimeoutCertificate>, | |||
/// Latest vote we have cast, to validate or confirm. | |||
#[debug(skip_if = Option::is_none)] | |||
pub pending: Option<Vote>, | |||
pub pending: Option<Vote<CertificateValue>>, |
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 timeout_vote
is already separate. We could split up the other two as well.
//TODO: Move | ||
pub trait Has<T> { | ||
fn get(&self) -> &T; | ||
} |
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.
Is it useful to be that general? Would a HasChainId
trait be more readable, since its method could simply be called chain_id
?
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.
Let's leave for now and if it turns out that there's only one version of it - I'll replace it.
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.
Please remove the TODO or add a task number
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.
Done in Replace TODO with a comment.
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.
Feeling a little bit like @afck here: I would probably choose the very explicit HasChainId
(which can use the fact that ChainId
is Copy
) and otherwise why not AsRef
.
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.
Soon we will need more instances for things like Block
and Epoch
(next PR will try to remove the CertificateValue
which has all of these methods defined on the enum).
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.
No blockers from my side; just still not sure about the Has<ChainId>
.
@@ -319,7 +319,11 @@ impl ChainManager { | |||
} | |||
} | |||
let value = HashedCertificateValue::new_timeout(chain_id, height, epoch); |
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.
Why not use a constructor for a Hashed<Timeout>
directly? (Also below.)
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.
b/c the hashes would change, and we still haven't done that.
actually let me check.
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.
Oh, we are currently instantiating Hashed<T>
s using new_unchecked
that actually don't match what Hashed::new
would do? 😬 Would be good to fix that as soon as possible and have only one way to hash things.
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.
With this change I get a test failure (without passes fine):
failures:
client::client_tests::test_request_leader_timeout::storage_service
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.
Oh, we are currently instantiating
Hashed<T>
s usingnew_unchecked
that actually don't match whatHashed::new
would do? 😬 Would be good to fix that as soon as possible and have only one way to hash things.
The only way to fix this is to get rid of CertificateValue
(and Certificate
) entirely.
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.
Exactly: We could serialize Timeout
as if it were a CertificateValue::Timeout
.
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.
do the transformation in the Serialize
(and Deserialize
)? Not a bad idea, I'll give it a try.
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.
And maybe not even do a transformation, but serialize a struct that only borrows the payload but is otherwise identical to CertificateValue
?
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.
Here we go again...
Running tests/format.rs (target/debug/deps/format-3b29b29e5fdad0f7)
running 1 test
test test_format ... FAILED
failures:
---- test_format stdout ----
thread 'test_format' panicked at linera-rpc/tests/format.rs:61:64:
called `Result::unwrap()` on an `Err` value: Custom("Failed to deserialize value: \"Expected a Timeout value\"")
This won't work. In the deserializer for Timeout
:
impl<'de> Deserialize<de> for TImeout {
// omit for brevity
let timeout = CertificateValue::deserialize(deserializer)?
the last line will "hint" the serde-reflection
to generate an instance of CertificateValue
enum but it will generate the first enum variant - CertificateValue::ValidatedBlock
and the format.yaml
test will fail. I think we need to get rid of that enum ASAP to get any further - right now a mix of an enum and how serde-reflection
behaves with enums is blocking us.
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.
And maybe not even do a transformation, but serialize a struct that only borrows the payload but is otherwise identical to
CertificateValue
?
CertifivateValue
is an enum so my "temporary struct" would have to reflect its structure b/c serde
(-reflection
) uses that to derive the binary format.
linera-chain/src/data_types.rs
Outdated
#[derive(Debug, Deserialize)] | ||
struct VoteHelper<T> { |
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.
This is going to make VoteHelper
appear in the recorded formats, disregarding T
. You need to use serde_name
for this.
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.
It's also a potential security vulnerability because we use the serde
-provided name as a seed for the hashing, but here the seed won't take T
into account here. It looks like the format is not recorded (because we're converting votes into legacy RPC messages?) so maybe it's not a blocker for this PR but you need to add TODO(#nnn)
with a task and we need to start doing the right thing asap.
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.
This is Deserialize
instance, not Serialize
(that one is still derived) so it doesn't appear in the logs?
because we're converting votes into legacy RPC messages?
We shouldn't 🤔 I haven't added any conversions for that IIRC.
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.
Point taken for Deserialize
-only. Curious what this is used for if there is no Serialize
implementation. (Hopefully we're not using serde
-serialization to recast types? :))
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.
Deserializing a Hashed<T>
adds a new trait bound T: BcsHashable
– b/c Hashed<_>
type does not serialize the hash, just the value, and we recreate the hash when deserializing. I didn't add this bound on the Vote<T>
struct itself for two reasons:
- trait bounds on structs are not good
- compiler complains about it anyway (see here for details)
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 realized that I actually can do this with derive but I need additional attributes - b4e8e64
{ | ||
LiteValue { | ||
value_hash: self.hash, | ||
chain_id: *Has::<ChainId>::get(&self.value), |
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 believe this would simply be self.value.chain_id()
with T: HasChainId
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.
Simplified in Simplify call to extract ChainId
Motivation
Vote
andSignatureAggregator
both work with hardcoded types (Certificate
andHashedCertificateValue
respectively) which is an obstacle to full removal ofCertificate
from the codebase.Proposal
Make both generic over type they work with.
Test Plan
CI should catch regressions.
Release Plan
Links
Closes #2885