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

Enable ChainReader to read PDA account state #1003

Merged
merged 17 commits into from
Jan 17, 2025

Conversation

amit-momin
Copy link
Contributor

@amit-momin amit-momin commented Jan 7, 2025

Enable the ChainReader to read PDA account state

  • Added a PDADefintion field to the read definition to allow us to configure the fields needed for a PDA account read
  • PDADefintion includes a Prefix and a Seeds field
    • Prefix is a static string that is converted to bytes and prepended to the list of seeds used for PDA calculation
    • Seeds is a list of seeds that are expected to be provided through params
  • Updated accountReadBinding's GetAddress method to intake params to use for seeds when calculating the PDA
  • Added a new method to accountReadBinding to encode and build the seeds list

Copy link
Contributor

@silaslenihan silaslenihan left a comment

Choose a reason for hiding this comment

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

This all looks great to me! Really readable and straightforward. Not sure if you're done adding functionality for CCIP but this should unblock the interface tests

))
var reader readBinding
// Create PDA read binding if Seeds config is non-nil
// Note: Empty seeds list is a valid configuration for a PDA so a length check is intentionally skipped
Copy link
Contributor

Choose a reason for hiding this comment

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

Would an empty seeds list just be a normal account read?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think FindProgramAddress still outputs a different programID even with empty seeds since it's still applying the bump seed. I think every PDA in the on-chain code uses some sort of seed though. So guess it wouldn't hurt to also validate empty seeds for our current use case but I wanted to keep this open in case something changes in the future.

pkg/solana/chainreader/pda_read_binding.go Outdated Show resolved Hide resolved
Comment on lines 397 to 399
case SeedPubKey:
typedCodec = NewPublicKey()
case SeedUint64:
Copy link
Contributor

Choose a reason for hiding this comment

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

why can't we reuse asStruct here so that every type is handled?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I was thinking asStruct seemed strongly tied to IDL types and since PDA seeds aren't officially defined in the IDL I thought it made sense to keep this separate. But your comments making me rethink that. I think I can use IDLField in place of SeedDefinition and reuse the existing IDLType instead of my custom enum. I'll give this a shot

var typedCodec commonencodings.TypeCodec
switch field.Type {
case SeedPubKey:
typedCodec = NewPublicKey()
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is special handling for public key needed? Why doesn't regular public key encoded by Solana codec work?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sorry this was just a miss on my part. I see this now which would get automatically picked up once I address the previous comment.

}
reader = newPdaReadBinding(namespace, genericName, readDefinition.PDAPrefix)
} else {
if err := s.addCodecDef(true, namespace, genericName, codec.ChainConfigTypeAccountDef, idl, codec.NilIdlTypeDefTy, readDefinition.InputModifications); err != nil {
Copy link
Contributor

@ilija42 ilija42 Jan 15, 2025

Choose a reason for hiding this comment

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

What does this do? There are no inputs for non PDA account reads

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think this is unused for normal account reads. For encoding PDAs, we only really need the new addSeedsCodecDef method I added. But when I tried to remove this, I started getting some errors. I can debug and see what's going on but since this was already in the existing code I opted to keep it as is

Copy link
Contributor

Choose a reason for hiding this comment

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

my bad actually, I wrote this code lol

Copy link
Contributor

@ilija42 ilija42 Jan 15, 2025

Choose a reason for hiding this comment

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

I thought I left it as a placeholder for PDA to be input, not sure why it throws errors

Comment on lines 252 to 268
func (s *SolanaChainReaderService) addSeedsCodecDef(namespace, genericName string, seedDefs []codec.SeedDefinition, modCfg commoncodec.ModifiersConfig) error {
mod, err := modCfg.ToModifier(codec.DecoderHooks...)
if err != nil {
return err
}
// Append seed suffix to differentiate the entry for encoding seeds from the account read entry
seedEntry, err := codec.NewSeedEntry(genericName, mod, seedDefs)
if err != nil {
return fmt.Errorf("failed to create a codec entry for seed definitions %v: %w", seedDefs, err)
}

// Seed codec entry is only used for encoding. Read type is not used by WrapItemType for encoding string.
s.parsed.EncoderDefs[codec.WrapItemType(true, namespace, genericName, "")] = seedEntry

return nil
}

Copy link
Contributor

@ilija42 ilija42 Jan 15, 2025

Choose a reason for hiding this comment

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

I think this can go away in favour of just using addCodecDef(). You just need to define seeds as a new IDL type and handle it like events, args and accounts are handled here CreateCodecEntry() which is a func used by addCodecDef().

@amit-momin amit-momin changed the title [WIP] Enable ChainReader to read PDA account state Enable ChainReader to read PDA account state Jan 16, 2025
@amit-momin amit-momin marked this pull request as ready for review January 16, 2025 18:57
@amit-momin amit-momin requested a review from a team as a code owner January 16, 2025 18:57
Copy link
Contributor

@ilija42 ilija42 left a comment

Choose a reason for hiding this comment

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

looks good

Copy link
Contributor

@EasterTheBunny EasterTheBunny left a comment

Choose a reason for hiding this comment

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

LGTM

@cl-sonarqube-production
Copy link

Quality Gate failed Quality Gate failed

Failed conditions
0.0% Coverage on New Code (required ≥ 75%)

See analysis details on SonarQube

@aalu1418 aalu1418 merged commit 933f88f into develop Jan 17, 2025
35 of 36 checks passed
@aalu1418 aalu1418 deleted the NONEVM-983-Implement-PDA-Seed-hasher branch January 17, 2025 20:16
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.

5 participants