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

Program macro to generate helper fns and documentation #10826

Closed

Conversation

CriesofCarrots
Copy link
Contributor

@CriesofCarrots CriesofCarrots commented Jun 27, 2020

Problem

There's a lot of information duplicated between program instruction enums and helper functions that build Instructions. Also, it's difficult to parse on-chain instructions and get useful information about the expected accounts, as that information is buried in the documentation in the aforementioned enum. Design proposal #10763 suggests addressing both issues by adding a procedural macro that would parse an annotated Instruction enum to generate needed helper functions as well as a 2nd enum that exposes account information. This would also have the benefit of enforcing consistency across program Instruction enum docs.

Summary of Changes

  • Add instructions attribute procedural macro

Toward #10476

Notes:

  • trybuild is a neat tool for catching compilation failures and ensuring error messages are helpful and descriptive. However, it's possible there could be issues with our monorepo due to Ensure tests are built with same dependencies as parent crate dtolnay/trybuild#53. (I ran into those bincode errors we saw on master recently due to a newer version of bincode in my .cargo/registry/cache being pulled into the trybuild compilation) If it seems flaky in CI or for devs, I'll remove
  • TODO:
    • Add skip variant-level attribute

@CriesofCarrots
Copy link
Contributor Author

@garious , @jackcmay, one thing I wanted to run by you:

I originally wrote the helper functions to use Instruction::new(), but that requires the program instruction enum to implement Serialize... spl_token, for example, does not. e104a09 updates the macro to require the enum to either derive Serialize or implement a custom serialize() method. Does this seem reasonable?

@codecov
Copy link

codecov bot commented Jun 27, 2020

Codecov Report

Merging #10826 into master will decrease coverage by 0.1%.
The diff coverage is 56.3%.

@@            Coverage Diff            @@
##           master   #10826     +/-   ##
=========================================
- Coverage    81.8%    81.7%   -0.2%     
=========================================
  Files         303      304      +1     
  Lines       71067    71406    +339     
=========================================
+ Hits        58194    58389    +195     
- Misses      12873    13017    +144     

@jackcmay
Copy link
Contributor

@CriesofCarrots @jstarry brought this up a while ago and contrary to my last comment in the issue I agree, the Instruction::new's built-in dependency on serializing is probably not what we want. I don't see why we should enforce any kind of structure on instruction data at this layer.
#5970

@CriesofCarrots
Copy link
Contributor Author

I don't see why we should enforce any kind of structure on instruction data at this layer.

The custom serialize isn't enforcing any kind of structure, just that the instruction have a serialize method of some kind.
If that is undesirable, the alternative is to have these generated helper functions expect Vec<u8> -- ie. already serialized data -- instead of a program instruction. That seems less clean to me, but more flexible.

@CriesofCarrots CriesofCarrots marked this pull request as ready for review June 29, 2020 19:25
@jackcmay
Copy link
Contributor

Taking a Vec<u8> or &[u8] seems cleaner to me. IMO the instruction should not assume any structure or form about data, enforcing Serialize trait makes unnecessary assumptions about the form or source of the data. To the instruction, the data should be a purely transparent chunk of bytes. The creator of the instruction should have a client/server type layer on top of instruction that concretely knows the structure and form of data.

@garious
Copy link
Contributor

garious commented Jun 29, 2020

Changing it to Vec<u8> is fine. The Serialize was meant to be a convenience feature, not a constraint.

@CriesofCarrots CriesofCarrots force-pushed the program-macros branch 2 times, most recently from d6bc72b to 07d8a07 Compare June 29, 2020 22:46
@CriesofCarrots
Copy link
Contributor Author

@garious , this is updated. Please let me know if there's something else you'd like to see before review.

pub enum TestInstruction {
/// Transfer lamports
#[accounts(
from_account(SIGNER, WRITABLE, desc = "Funding account"),
Copy link
Contributor

Choose a reason for hiding this comment

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

Looking back at the proposal, I see this syntax made it in, but I still find it really confusing. Remind me why we can't write (from_account, SIGNER | WRITABLE, desc = "Funding account")?

Copy link
Contributor Author

@CriesofCarrots CriesofCarrots Jun 30, 2020

Choose a reason for hiding this comment

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

This is using the standard attribute syntax for a list (of accounts/names), which each carry a list of account attributes.
In addition to the "standardization" aspect, it's significantly easier to parse the attributes in the macro if they can be mapped into syn library Meta types.

But if you feel really strongly about this, I'll write a custom parser.

One thing that I'm not crazy about with your proposed syntax is the strict ordering that's required inside the parens in order to identify the account name. As it is, the account name is unambiguous, because it is the item Path, and account attributes inside the parens can be listed in any order and remain parsable.

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm looking for as little custom parsing as possible and that wherever possible, pass in Rust expressions. I don't want to learn a new DSL to annotate this enum.

Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe this is possible?

use solana_sdk_program_macros::{SIGNER, WRITABLE};
...
#[accounts(
       (from_account, SIGNER | WRITABLE, desc: "Funding account")
  )]

So a tuple of an identifier, an expression that evaluates to a u64, and then key-value pairs that use the struct field syntax.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure, it's possible. I think this is approx the same amount of extra handling as your previous version.

I'm not sure whether it's easy to actually evaluate the SIGNER | WRITABLE expression in the macro. I'll have to look into that.

instruction::{AccountMeta, Instruction},
pubkey::Pubkey,
};
use solana_sdk_program_macros::instructions;
Copy link
Contributor

Choose a reason for hiding this comment

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

Why no use solana_sdk_program_macros::{SIGNER, WRITABLE}; in here?

@stale
Copy link

stale bot commented Jul 8, 2020

This pull request has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs.

@stale stale bot added the stale [bot only] Added to stale content; results in auto-close after a week. label Jul 8, 2020
@CriesofCarrots CriesofCarrots removed the stale [bot only] Added to stale content; results in auto-close after a week. label Jul 11, 2020
@stale
Copy link

stale bot commented Jul 18, 2020

This pull request has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs.

@stale stale bot added the stale [bot only] Added to stale content; results in auto-close after a week. label Jul 18, 2020
@CriesofCarrots CriesofCarrots removed the stale [bot only] Added to stale content; results in auto-close after a week. label Jul 18, 2020
@stale
Copy link

stale bot commented Jul 25, 2020

This pull request has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs.

@stale stale bot added the stale [bot only] Added to stale content; results in auto-close after a week. label Jul 25, 2020
@stale
Copy link

stale bot commented Aug 1, 2020

This stale pull request has been automatically closed. Thank you for your contributions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
stale [bot only] Added to stale content; results in auto-close after a week.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants