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

Generic Transaction Trait #1918

Merged
merged 26 commits into from
Jul 12, 2024
Merged

Generic Transaction Trait #1918

merged 26 commits into from
Jul 12, 2024

Conversation

apfitzge
Copy link

@apfitzge apfitzge commented Jun 28, 2024

Problem

  • The use of SanitizedTransaction is extremely slow in block-production
    • This is mainly due to the high level of nested allocations.
    • Legacy messages use 3 + 2 * num_instructions allocations
    • V0 messages use 4 + 2 * num_instructions + 2 * num_lookups
  • This has motivated the use of a new transaction type, however runtime & SVM do not support using anything other than SanitizedTransaction.
  • It would be convenient if we could slowly migrate to new transaction type, and have the runtime and SVM be generic over some transaction trait

Summary of Changes

  • Create 2 new traits: MessageTrait, TransactionTrait
    • MessageTrait is effectively to abstract current SanitizedMessage
    • TransactionTrait is an extension of MessageTrait that is effectively to abstract SanitizedTransaction
  • Implemented trait for the "reference" types SanitizedMessage and SanitizedTransaction
  • Implemented trait(s) for &T if T implements traits
    • this allows us to pass owned values or references to any function that is generic over the trait...this is really convenient
    • makes some existing code simpler: no more impl core::borrow::Borrow<SanitizedTransaction>
  • New crate and trait names are placeholders, will be using this PR as a platform to discuss naming. Possible trait names:
    • MessageTrait, TransactionTrait
    • Message, SignedMessage (@Lichtso had mentioned to me that he felt we use "transaction" incorrectly right now, and a "transaction" is a loaded message. i.e. users always send messages, transactions are what come out of those messages).
  • Some possible crate names (probably depends on above choice):
    • solana-transaction-trait
    • solana-signed-message

Fixes #

@apfitzge
Copy link
Author

apfitzge commented Jun 28, 2024

@LucasSte @t-nelson @Lichtso @buffalojoec

Could I get input on naming here? I have a few options in the PR description, or feel free to submit your own idea(s)

Another open question is whether or not the traits should live inside of SVM or as a separate crate like I've done above.

@buffalojoec
Copy link

I'm in favor of this! I also think it should be its own crate, as you have it.

My thoughts on naming: solana-transaction (crate), SolanaMessage and SolanaTransaction. Maybe also SolanaInstruction.

@apfitzge
Copy link
Author

I'm in favor of this! I also think it should be its own crate, as you have it.

My thoughts on naming: solana-transaction (crate), SolanaMessage and SolanaTransaction. Maybe also SolanaInstruction.

I like these more than the current names I have

@kevinheavey
Copy link

kevinheavey commented Jun 29, 2024

If we call this solana-transaction what do we call the crate we make when solana_sdk::transaction finally gets its own crate?

Fully support making this a new crate whatever we call it. I suggest solana-transaction-trait

@buffalojoec
Copy link

If we call this solana-transaction what do we call the crate we make when solana_sdk::transaction finally gets its own crate?

Fully support making this a new crate whatever we call it. I suggest solana-transaction-trait

This is just my own bias, but I don't like putting "trait" or "struct" in the names, I'd lean toward solana-transaction-interface if we didn't want to nab solana-transaction. They could also be the same crate.

@kevinheavey
Copy link

kevinheavey commented Jun 29, 2024

They could also be the same crate.

If they were the same crate then there would be more chance of users getting confused by the Instruction and MessageAddressTableLookup structs that this crate has.

Plus there'd be more bloat and it would be harder to rip out what's needed for transactions from solana-sdk, e.g. this crate requires solana_sdk::packet, which is not required by solana_sdk::transaction

@LucasSte
Copy link

I'm in favor of this! I also think it should be its own crate, as you have it.

My thoughts on naming: solana-transaction (crate), SolanaMessage and SolanaTransaction. Maybe also SolanaInstruction.

These names are good. I couldn't think of anything better (I'm terrible at naming things).

@apfitzge
Copy link
Author

apfitzge commented Jul 1, 2024

This is just my own bias, but I don't like putting "trait" or "struct" in the names, I'd lean toward solana-transaction-interface if we didn't want to nab solana-transaction. They could also be the same crate.

I actually want solana-transaction-interface for something else in the near future 😉 (an ffi capable interface for transactions). Which is kind of dependent on this work actually.

I think whatever name we choose, this should be a separate crate from the sdk transaction.


/// Returns the message hash.
// TODO: consider moving this to Message
fn message_hash(&self) -> &Hash;
Copy link
Author

Choose a reason for hiding this comment

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

for SanitizedTransaction this just fetches a pre-computed message hash, which is why i'd put it here.
That's a current implementation detail and I feel doesn't make too much sense honestly.

Could also override the impl for SanitizedTransaction to get the cached value, while calculating it on SanitizedMessage and then move this to the MessageTrait


/// Returns true if the transaction is a simple vote transaction.
// TODO: consider moving this to Message
fn is_simple_vote_transaction(&self) -> bool;
Copy link
Author

Choose a reason for hiding this comment

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

same comment here as for message_hash

Copy link
Author

Choose a reason for hiding this comment

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

ended up removing these 2 functions + conversion to VersionedTransaction.

Trimmed the traits down to what is necessary for SVM, and will have some extension that can be used inside core/runtime.

@buffalojoec
Copy link

It would be convenient if we could slowly migrate to new transaction type, and have the runtime and SVM be generic over some transaction trait

I guess it wouldn't hurt to at least consider SvmTransaction and SvmMessage.

They don't look as nice as SolanaTransaction and SolanaMessage, but perhaps the "SVM" prefix describes the traits' intentions more. Thinking a bit about people using solana-svm outside of the validator.

If we call this solana-transaction what do we call the crate we make when solana_sdk::transaction finally gets its own crate?

Not sure if this makes the relationship any more clear, but you could have solana-svm-transaction for the interface and solana-transaction for the SDK structs that implement it. It might appear a bit backwards, but people working with Solana tooling on the chain itself would just use solana_transaction::Transaction, meanwhile SVM projects would spot solana_svm_transaction::SvmTransaction in the solana-svm library and see they can implement it with a custom transaction.

Or maybe it's even solana_svm_transaction::SvmTransaction and solana_transaction::SolanaTransaction. Seems a tad redundant, though.

@apfitzge
Copy link
Author

apfitzge commented Jul 2, 2024

I guess it wouldn't hurt to at least consider SvmTransaction and SvmMessage.

This was my original idea, but I'm a bit hesistant since there are a few things in the interface that are not strictly necessary for SVM, but are there for either the use inside of Runtime or Core.

@kevinheavey
Copy link

Put me on record as strongly opposed to calling the new crate solana-transaction for the reasons above

@apfitzge
Copy link
Author

apfitzge commented Jul 2, 2024

ended up just going with solana-svm-transaction, SVMMessage and SVMTransaction.

nit on myself: any preference for SVM vs Svm in trait naming? Seems "proper" to do SVM, but also a bit unwieldy to read MMessage

@apfitzge apfitzge marked this pull request as ready for review July 2, 2024 20:47
Copy link

@buffalojoec buffalojoec left a comment

Choose a reason for hiding this comment

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

Nice, looking great! I left a few comments.

solana-program = { workspace = true }
solana-sdk = { workspace = true }

Choose a reason for hiding this comment

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

You only need solana-sdk here, since it re-exports all of solana-program.

svm-transaction/src/lib.rs Outdated Show resolved Hide resolved
svm-transaction/src/svm_transaction/reference.rs Outdated Show resolved Hide resolved
svm-transaction/src/svm_transaction.rs Outdated Show resolved Hide resolved
svm-transaction/src/svm_message/reference.rs Outdated Show resolved Hide resolved
@apfitzge
Copy link
Author

apfitzge commented Jul 3, 2024

I'll raise a question here about a related change.

There are several convenience functions that I plan to implement which use this trait, but imo don't belong on the trait itself.
These are largely fns that are useful within our codebase rather than something that needs to be public for using SVM, and I think ideally we'd either version it separately from this trait crate.

Some examples of functions I anticipate adding to that new module/crate:

    fn writable_accounts_iter(message: &impl SVMMessage) -> impl Iterator<Item = &Pubkey> + Clone;
    fn readable_accounts_iter(message: &impl SVMMessage) -> impl Iterator<Item = &Pubkey> + Clone;
    fn verify_precompiles(message: &impl SVMMessage) -> Result<(), TransactionError>;
    fn get_durable_nonce(message: &impl SVMMessage) -> Option<&Pubkey>;
    fn get_signature_details(message: &impl SVMMessage) -> TransactionSignatureDetails;

My gut feeling is these belong in some dependent crate, but wonder if you all think these belong in the same crate?

@buffalojoec
Copy link

There are several convenience functions that I plan to implement which use this trait, but imo don't belong on the trait itself. These are largely fns that are useful within our codebase rather than something that needs to be public for using SVM, and I think ideally we'd either version it separately from this trait crate.

I'd agree and lean toward a separate crate - maybe something like solana-svm-transaction-extractors. Just be careful not to end up with a miscellaneous kitchen-sink of utils.

@apfitzge
Copy link
Author

apfitzge commented Jul 11, 2024

@buffalojoec @LucasSte I removed a few extra things from SVMTransaction; it only has the accessors for signatures.
I think the other stuff can be an extended trait that is only used inside of core/runtime. Since we never use that stuff inside of SVM I think that makes more sense.

@apfitzge apfitzge requested a review from buffalojoec July 11, 2024 20:18
Copy link

@buffalojoec buffalojoec left a comment

Choose a reason for hiding this comment

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

Nice, lgtm!

I think the other stuff can be an extended trait that is only used inside of core/runtime. Since we never use that stuff inside of SVM I think that makes more sense.

Yep, I agree. This makes more sense.

@apfitzge apfitzge merged commit a191a77 into anza-xyz:master Jul 12, 2024
51 checks passed
@apfitzge apfitzge deleted the svm_transaction branch July 12, 2024 14:44
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.

4 participants