fix: use proper serialization for versioned messages in get_fee_for_m…#7719
Conversation
|
@KirillLykov would you know the right person to review this? |
|
@steveluscher probably is the best one |
|
Also this PR seems to fix the problem, it introduces some additional code. I wonder if it is possible to reuse |
Good point! The issue is that The key difference:
We could modify |
|
@swarna1101 Hm, I got you. PS CI fails with extra whitespaces: |
I've refactored to use the existing |
| Ok(encoded) | ||
| } | ||
|
|
||
| fn serialize_and_encode_versioned_message( |
There was a problem hiding this comment.
Are you sure that we need a new serialize_and_encode_versioned_message function? It looks to me serialize_and_encode will work, won't it?
|
|
||
| #[test] | ||
| fn test_versioned_message_serialization_includes_prefix() { | ||
| use solana_message::{v0, Message as LegacyMessage, compiled_instruction::CompiledInstruction, v0::MessageAddressTableLookup, MessageHeader}; |
There was a problem hiding this comment.
I think we typically avoid using function-level imports.
There was a problem hiding this comment.
Removed all function-level use statements from the test functions since the required types are already imported in the test module's use block
| fn test_versioned_message_serialization_includes_prefix() { | ||
| use solana_message::{v0, Message as LegacyMessage, compiled_instruction::CompiledInstruction, v0::MessageAddressTableLookup, MessageHeader}; | ||
|
|
||
| // Test that VersionedMessage serialization includes MESSAGE_VERSION_PREFIX |
There was a problem hiding this comment.
I would move this comment to be for the whole test, so above #[test]
| message: &VersionedMessage, | ||
| ) -> ClientResult<u64> { | ||
| let serialized_encoded = serialize_and_encode(message, UiTransactionEncoding::Base64)?; | ||
| let serialized_encoded = serialize_and_encode_versioned_message(message, UiTransactionEncoding::Base64)?; |
There was a problem hiding this comment.
Do you know if there are tests for this get_fee_for_message function? If they exist and easy to add a new one, I think it might be valuable to have tests for this function instead of serialization of VersionedMessage because the later lives in sdk.
There was a problem hiding this comment.
You're right that we should test the actual get_fee_for_message function rather than just serialization since VersionedMessage is already well-tested in the SDK.
Currently there aren't specific tests for get_fee_for_message - adding proper ones would need either integration tests with a real RPC server or mock-based tests.
Since this PR focuses on the serialization bug fix and our tests do validate the fix works (v0 messages get proper version prefix), I think they serve their purpose here. But I agree comprehensive function tests would be valuable as a follow-up.
Would you prefer adding mock tests in this PR or keeping this focused and doing function tests separately?
There was a problem hiding this comment.
I would add a unit test with mock rpc as part of the bug fix (this one) but for backporting it to 2.3.x, it is might be better to make a follow up so this PR is without tests.
There was a problem hiding this comment.
got it, will be great if we can create an issue to track it, happy to do that
|
Backports to the stable branch are to be avoided unless absolutely necessary for fixing bugs, security issues, and perf regressions. Changes intended for backport should be structured such that a minimum effective diff can be committed separately from any refactoring, plumbing, cleanup, etc that are not strictly necessary to achieve the goal. Any of the latter should go only into master and ride the normal stabilization schedule. |
|
@jstarry, do you still have the versioned message stuff loaded into your head enough that this is a quick review for you? |
|
Backports to the beta branch are to be avoided unless absolutely necessary for fixing bugs, security issues, and perf regressions. Changes intended for backport should be structured such that a minimum effective diff can be committed separately from any refactoring, plumbing, cleanup, etc that are not strictly necessary to achieve the goal. Any of the latter should go only into master and ride the normal stabilization schedule. Exceptions include CI/metrics changes, CLI improvements and documentation updates on a case by case basis. |
steveluscher
left a comment
There was a problem hiding this comment.
Your original approach of trying to teach SerializableMessage to do the right thing was less intrusive, and required fewer changes. I think the current version of this PR where you force folks to use get_fee_for_message_with_legacy sometimes and get_fee_for_message other times is worse.
With a few tweaks, wouldn't your original change work?
diff --git a/rpc-client/src/nonblocking/rpc_client.rs b/rpc-client/src/nonblocking/rpc_client.rs
index e4e1396abc..293228b07b 100644
--- a/rpc-client/src/nonblocking/rpc_client.rs
+++ b/rpc-client/src/nonblocking/rpc_client.rs
@@ -4671,7 +4671,8 @@ impl RpcClient {
&self,
message: &impl SerializableMessage,
) -> ClientResult<u64> {
- let serialized_encoded = serialize_and_encode(message, UiTransactionEncoding::Base64)?;
+ let serialized = message.serialize();
+ let serialized_encoded = BASE64_STANDARD.encode(serialized);
let result = self
.send::<Response<Option<u64>>>(
RpcRequest::GetFeeForMessage,
diff --git a/rpc-client/src/rpc_client.rs b/rpc-client/src/rpc_client.rs
index bf42e2fb06..567fc44ba0 100644
--- a/rpc-client/src/rpc_client.rs
+++ b/rpc-client/src/rpc_client.rs
@@ -62,9 +62,19 @@ impl RpcClientConfig {
/// Trait used to add support for versioned messages to RPC APIs while
/// retaining backwards compatibility
-pub trait SerializableMessage: Serialize {}
-impl SerializableMessage for LegacyMessage {}
-impl SerializableMessage for v0::Message {}
+pub trait SerializableMessage {
+ fn serialize(&self) -> Vec<u8>;
+}
+impl SerializableMessage for LegacyMessage {
+ fn serialize(&self) -> Vec<u8> {
+ self.serialize()
+ }
+}
+impl SerializableMessage for v0::Message {
+ fn serialize(&self) -> Vec<u8> {
+ self.serialize()
+ }
+}
/// Trait used to add support for versioned transactions to RPC APIs while
/// retaining backwards compatibility
@@ -3803,6 +3813,7 @@ mod tests {
serde_json::{json, Number},
solana_account_decoder::encode_ui_account,
solana_account_decoder_client_types::UiAccountEncoding,
+ solana_hash::Hash,
solana_instruction::error::InstructionError,
solana_keypair::Keypair,
solana_rpc_client_api::client_error::ErrorKind,|
Ugh, looks like I introduced this bug in solana-labs#28217 because I didn't add proper tests, apologies! I think that we should do a few things:
|
|
@steveluscher I don't really understand how your suggestion would fix the issue. Where is the version prefix handled? |
Here's a one-liner that applies the patch, after which you can hyperclick around the calls to |
|
Thanks a lot for all this discussions, I am checking them, let me get back |
9904aa8 to
5c486b2
Compare
|
This PR has been rewritten, and a tight test case added to assert that what we're sending to |
5c486b2 to
cb41b5c
Compare
|
But it fails CI |
Co-authored-by: kirill lykov <lykov.kirill@gmail.com>
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## master #7719 +/- ##
=======================================
Coverage 82.9% 82.9%
=======================================
Files 823 823
Lines 360694 360763 +69
=======================================
+ Hits 299159 299237 +78
+ Misses 61535 61526 -9 🚀 New features to boost your workflow:
|
| &self, | ||
| message: &impl SerializableMessage, | ||
| ) -> ClientResult<u64> { | ||
| let serialized_encoded = serialize_and_encode(message, UiTransactionEncoding::Base64)?; |
There was a problem hiding this comment.
Isn't new code does the same as the function serialize_and_encode but just in place? I wonder if we even need this function if it is a 2 lines of code.
There was a problem hiding this comment.
Not exactly. serialize_and_encode does bincode::serialize(thing) whereas the change does thing.serialize(). I'm always happy inlining code, but that's a matter for a different PR.
KirillLykov
left a comment
There was a problem hiding this comment.
the change itself looks good to me, left comment about the function serialize_and_encode.
#7719) * fix: use proper serialization for versioned messages in get_fee_for_message * Clippy satiated Co-authored-by: kirill lykov <lykov.kirill@gmail.com> * Try harder to satiate clippy --------- Co-authored-by: Steven Luscher <steveluscher@users.noreply.github.com> Co-authored-by: kirill lykov <lykov.kirill@gmail.com> (cherry picked from commit 3f25767)
#7719) * fix: use proper serialization for versioned messages in get_fee_for_message * Clippy satiated Co-authored-by: kirill lykov <lykov.kirill@gmail.com> * Try harder to satiate clippy --------- Co-authored-by: Steven Luscher <steveluscher@users.noreply.github.com> Co-authored-by: kirill lykov <lykov.kirill@gmail.com> (cherry picked from commit 3f25767)
…_for_m… (backport of #7719) (#8138) fix: use proper serialization for versioned messages in get_fee_for_m… (#7719) * fix: use proper serialization for versioned messages in get_fee_for_message * Clippy satiated * Try harder to satiate clippy --------- (cherry picked from commit 3f25767) Co-authored-by: Swarna <swarnabhasinha@gmail.com> Co-authored-by: Steven Luscher <steveluscher@users.noreply.github.com> Co-authored-by: kirill lykov <lykov.kirill@gmail.com>
…_for_m… (backport of #7719) (#8139) fix: use proper serialization for versioned messages in get_fee_for_m… (#7719) * fix: use proper serialization for versioned messages in get_fee_for_message * Clippy satiated * Try harder to satiate clippy --------- (cherry picked from commit 3f25767) Co-authored-by: Swarna <swarnabhasinha@gmail.com> Co-authored-by: Steven Luscher <steveluscher@users.noreply.github.com> Co-authored-by: kirill lykov <lykov.kirill@gmail.com>
Fix versioned message serialization in
get_fee_for_messageProblem
get_fee_for_messagewas failing with "index out of bounds" errors when used with v0 messages containing Address Lookup Tables. The root cause was thatbincode::serialize()doesn't include theMESSAGE_VERSION_PREFIX(0x80) required for v0 messages, causing the RPC endpoint to misinterpret the serialized message format.Solution
Added a
serialize_message()method to theSerializableMessagetrait that uses the proper native serialization for each message type. For v0 messages, this includes the required version prefix, while legacy messages remain unchanged. Updatedget_fee_for_message()to use this new serialization method instead ofbincode::serialize().The fix ensures that both legacy messages and v0 messages with Address Lookup Tables are properly serialized when calling
get_fee_for_message(). Added comprehensive tests to verify the serialization behavior and ensure no regressions.Closes #7563
Can you pls check this @KirillLykov @0xbrw