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

Node/Solana: Shim watcher cleanup #4269

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

bruce-riley
Copy link
Contributor

No description provided.

@bruce-riley bruce-riley force-pushed the node/solana_shim_watcher_cleanup branch from 3a90296 to 6595ed9 Compare February 18, 2025 17:43
@bruce-riley bruce-riley force-pushed the node/solana_shim_watcher_cleanup branch from 6595ed9 to 311656f Compare February 26, 2025 14:45
@bruce-riley bruce-riley marked this pull request as ready for review February 26, 2025 15:04
@bruce-riley bruce-riley force-pushed the node/solana_shim_watcher_cleanup branch from 311656f to 8e21228 Compare March 3, 2025 15:19
Copy link
Contributor

@johnsaigle johnsaigle left a comment

Choose a reason for hiding this comment

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

I think some of the error handling is a little too general and may cause some bugs down the line. Otherwise, I made some suggestions to improve error messages and fix typos in test names.

for idx := startIdx; idx < len(innerInstructions); idx++ {
inst := innerInstructions[idx]
if inst.ProgramIDIndex == whProgramIndex {
if verifiedCoreEvent, err = shimVerifyCoreMessage(inst.Data); err != nil {
if verifiedCoreEvent {
return fmt.Errorf("detected multiple inner core instructions when there should not be at instruction %d, %d", outerIdx, idx)
Copy link
Contributor

Choose a reason for hiding this comment

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

Right now I believe this check is too general: this returns an error if the inner instruction's program index matches the Wormhole Program's, regardless of the contents. I think instead what should be flagged here is the case where another "verified core event" occurs.

So the more targeted check would be something like:

// Ensure that only one verified core event appears in each set of inner instructions
if foundIt && verifiedCoreEvent {
 // return error 
 }

In practice this might be fine, but maybe there's a valid case (now or down the line) where the Wormhole program needs to be interacted with twice in the same inner instruction set.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yeah, it looks pretty restrictive to me in the sense that any sequence of instructions where there's an instruction that returns true on shimVerifyCoreMessage(inst.Data), and then contains any subsequent instruction where the following condition holds inst.ProgramIDIndex == whProgramIndex, will result in an error.

alreadyProcessed.add(outerIdx, idx)
coreEventFound = true
if foundIt {
alreadyProcessed.add(outerIdx, idx)
Copy link
Contributor

Choose a reason for hiding this comment

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

I think it's OK to add this the instruction to the alreadyProcessed set even if foundIt is false.

} else if inst.ProgramIDIndex == shimProgramIndex {
if !coreEventFound {
if messageEvent != nil {
return fmt.Errorf("detected multiple shim message event instructions when there should not be at instruction %d, %d", outerIdx, idx)
Copy link
Contributor

Choose a reason for hiding this comment

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

Same as the case above -- this will catch and throw an error for any interaction with the shim program, even if this isn't a second shim event. So, this code might end up looking like this:

		} else if inst.ProgramIDIndex == shimProgramIndex {
			if !verifiedCoreEvent {
				return fmt.Errorf("detected an inner shim message event instruction before the core event for shim instruction %d, %d: %w", outerIdx, idx, err)
			}
			thisEvent, err := shimParseMessageEvent(s.shimMessageEventDiscriminator, inst.Data)
			if err != nil {
				return fmt.Errorf("failed to parse inner shim message event instruction for shim instruction %d, %d: %w", outerIdx, idx, err)
			}
			if messageEvent != nil && thisEvent != nil && len(thisEvent) >  0 {
				return fmt.Errorf("detected multiple shim message event instructions when there should not be at instruction %d, %d", outerIdx, idx)
			}
			alreadyProcessed.add(outerIdx, idx)
		}

@@ -290,16 +311,21 @@ func (s *SolanaWatcher) shimProcessRest(
}

if verifiedCoreEvent && messageEvent != nil {
break
if !isTopLevel {
// We found what we are looking for, and additional shim events in the inner instruction case are allowed, so we can stop looking.
Copy link
Contributor

Choose a reason for hiding this comment

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

Shouldn't the loop continue in case there are multiple verifiedCoreEvent occurrences? If not, it would be good to reword this to something like "In the integrator (inner instruction) case, additional core event and shim event pairs are allowed."

Basically my concern with this function is to make sure that:

  • there's a one-to-one correspondance of core events and shim events in all cases (i.e. they're always matched)
  • there are no "extra events" -- e.g. in the Direct/top-level case, there should only be one core event and one shim event

@@ -1007,3 +1003,1284 @@ func TestShimFromIntegratorWithMultipleShimTransactions(t *testing.T) {
assert.False(t, msg.IsReobservation)
assert.False(t, msg.Unreliable)
}

func TestShimDirectWithExtraWhEventBeforeShimEventShouldFails(t *testing.T) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
func TestShimDirectWithExtraWhEventBeforeShimEventShouldFails(t *testing.T) {
func TestShimDirectWithExtraWhEventBeforeShimEventShouldFail(t *testing.T) {

require.Equal(t, 2, len(alreadyProcessed)) // The first core and shim events will have been added.
}

func TestShimTopLevelShouldFailIfNoInstructionsShouldFail(t *testing.T) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
func TestShimTopLevelShouldFailIfNoInstructionsShouldFail(t *testing.T) {
func TestShimTopLevelEmptyInstructionsShouldFail(t *testing.T) {

Just a suggestion to remove the duplicate use of ShouldFail

require.Equal(t, 0, len(alreadyProcessed))
}

func TestShimInnerShouldFailIfNoInstructionsShouldFail(t *testing.T) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
func TestShimInnerShouldFailIfNoInstructionsShouldFail(t *testing.T) {
func TestShimProcessInnerInstructions_OutOfBoundsStartIndexShouldFail(t *testing.T) {

This name is super long but the point is just to clarify that what's being tested here is the "start index" value relative to the length of the inner instructions.

@@ -226,6 +230,10 @@ func (s *SolanaWatcher) shimProcessInnerInstruction(
alreadyProcessed ShimAlreadyProcessed,
isReobservation bool,
) (bool, error) {
if startIdx >= len(innerInstructions) {
return false, fmt.Errorf("startIdx %d is greater than or equal to %d", startIdx, len(innerInstructions))
Copy link
Contributor

@johnsaigle johnsaigle Mar 6, 2025

Choose a reason for hiding this comment

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

Suggested change
return false, fmt.Errorf("startIdx %d is greater than or equal to %d", startIdx, len(innerInstructions))
return false, fmt.Errorf("startIdx %d is out of bounds of slice innerInstructions (length: %d)", startIdx, len(innerInstructions))

From the error log it's not clear why e.g. "startIdx 3 is greater than or equal to 3" is a problem

@@ -175,6 +176,9 @@ func (s *SolanaWatcher) shimProcessTopLevelInstruction(
isReobservation bool,
) (bool, error) {
topLevelIdx := uint16(topLevelIndex)
if topLevelIdx >= uint16(len(tx.Message.Instructions)) {
return false, fmt.Errorf("topLevelIndex %d is greater than %d", topLevelIdx, len(tx.Message.Instructions))
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
return false, fmt.Errorf("topLevelIndex %d is greater than %d", topLevelIdx, len(tx.Message.Instructions))
return false, fmt.Errorf("topLevelIndex %d is greater than the total number of instructions in the tx message %d", topLevelIdx, len(tx.Message.Instructions))

for idx := startIdx; idx < len(innerInstructions); idx++ {
inst := innerInstructions[idx]
if inst.ProgramIDIndex == whProgramIndex {
if verifiedCoreEvent, err = shimVerifyCoreMessage(inst.Data); err != nil {
if verifiedCoreEvent {
return fmt.Errorf("detected multiple inner core instructions when there should not be at instruction %d, %d", outerIdx, idx)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Yeah, it looks pretty restrictive to me in the sense that any sequence of instructions where there's an instruction that returns true on shimVerifyCoreMessage(inst.Data), and then contains any subsequent instruction where the following condition holds inst.ProgramIDIndex == whProgramIndex, will result in an error.

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