-
Notifications
You must be signed in to change notification settings - Fork 737
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -29,6 +29,7 @@ package solana | |||||
import ( | ||||||
"bytes" | ||||||
"encoding/hex" | ||||||
"errors" | ||||||
"fmt" | ||||||
"time" | ||||||
|
||||||
|
@@ -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)) | ||||||
} | ||||||
inst := tx.Message.Instructions[topLevelIdx] | ||||||
|
||||||
// The only top-level instruction generated by the shim contract is the PostMessage event. Parse that to get | ||||||
|
@@ -192,7 +196,7 @@ func (s *SolanaWatcher) shimProcessTopLevelInstruction( | |||||
outerIdx := -1 | ||||||
for idx, inner := range innerInstructions { | ||||||
if inner.Index == topLevelIdx { | ||||||
outerIdx = int(idx) | ||||||
outerIdx = idx | ||||||
break | ||||||
} | ||||||
} | ||||||
|
@@ -202,7 +206,7 @@ func (s *SolanaWatcher) shimProcessTopLevelInstruction( | |||||
} | ||||||
|
||||||
// Process the inner instructions associated with this shim top-level instruction and produce an observation event. | ||||||
err = s.shimProcessRest(logger, whProgramIndex, shimProgramIndex, tx, innerInstructions[outerIdx].Instructions, outerIdx, 0, postMessage, alreadyProcessed, isReobservation) | ||||||
err = s.shimProcessRest(logger, whProgramIndex, shimProgramIndex, tx, innerInstructions[outerIdx].Instructions, outerIdx, 0, postMessage, alreadyProcessed, isReobservation, true) | ||||||
if err != nil { | ||||||
return false, fmt.Errorf("failed to process inner instructions for top-level shim instruction %d: %w", topLevelIdx, err) | ||||||
} | ||||||
|
@@ -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)) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
From the error log it's not clear why e.g. "startIdx 3 is greater than or equal to 3" is a problem |
||||||
} | ||||||
|
||||||
// See if this is a PostMessage event from the shim contract. If so, parse it. If not, bail out now. | ||||||
postMessage, err := shimParsePostMessage(s.shimPostMessageDiscriminator, innerInstructions[startIdx].Data) | ||||||
if err != nil { | ||||||
|
@@ -238,7 +246,7 @@ func (s *SolanaWatcher) shimProcessInnerInstruction( | |||||
|
||||||
alreadyProcessed.add(outerIdx, startIdx) | ||||||
|
||||||
err = s.shimProcessRest(logger, whProgramIndex, shimProgramIndex, tx, innerInstructions, outerIdx, startIdx+1, postMessage, alreadyProcessed, isReobservation) | ||||||
err = s.shimProcessRest(logger, whProgramIndex, shimProgramIndex, tx, innerInstructions, outerIdx, startIdx+1, postMessage, alreadyProcessed, isReobservation, false) | ||||||
if err != nil { | ||||||
return false, fmt.Errorf("failed to process inner instructions for inner shim instruction %d: %w", outerIdx, err) | ||||||
} | ||||||
|
@@ -260,7 +268,12 @@ func (s *SolanaWatcher) shimProcessRest( | |||||
postMessage *ShimPostMessageData, | ||||||
alreadyProcessed ShimAlreadyProcessed, | ||||||
isReobservation bool, | ||||||
isTopLevel bool, | ||||||
) error { | ||||||
if postMessage == nil { | ||||||
return errors.New("postMessage is nil") | ||||||
} | ||||||
|
||||||
// Loop through the inner instructions after the shim PostMessage and do the following: | ||||||
// 1) Find the core event and verify it is unreliable with an empty payload. | ||||||
// 2) Find the shim MessageEvent to get the rest of the fields we need for the observation. | ||||||
|
@@ -269,17 +282,25 @@ func (s *SolanaWatcher) shimProcessRest( | |||||
var verifiedCoreEvent bool | ||||||
var messageEvent *ShimMessageEventData | ||||||
var err error | ||||||
coreEventFound := false | ||||||
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) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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:
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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
||||||
} | ||||||
foundIt, err := shimVerifyCoreMessage(inst.Data) | ||||||
if err != nil { | ||||||
return fmt.Errorf("failed to verify inner core instruction for shim instruction %d, %d: %w", outerIdx, idx, err) | ||||||
} | ||||||
alreadyProcessed.add(outerIdx, idx) | ||||||
coreEventFound = true | ||||||
if foundIt { | ||||||
alreadyProcessed.add(outerIdx, idx) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
||||||
verifiedCoreEvent = true | ||||||
} | ||||||
} 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) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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:
|
||||||
} | ||||||
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) | ||||||
} | ||||||
messageEvent, err = shimParseMessageEvent(s.shimMessageEventDiscriminator, inst.Data) | ||||||
|
@@ -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. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't the loop continue in case there are multiple Basically my concern with this function is to make sure that:
|
||||||
break | ||||||
} | ||||||
|
||||||
// In the top level instruction case, we should not have any additional shim events in this instruction set. | ||||||
} | ||||||
} | ||||||
|
||||||
if !verifiedCoreEvent { | ||||||
return fmt.Errorf("failed to find inner core instruction for shim instruction %d", outerIdx) | ||||||
return fmt.Errorf("failed to find inner core instruction for shim instruction %d, %d", outerIdx, startIdx) | ||||||
} | ||||||
|
||||||
if messageEvent == nil { | ||||||
return fmt.Errorf("failed to find inner shim message event instruction for shim instruction %d", outerIdx) | ||||||
return fmt.Errorf("failed to find inner shim message event instruction for shim instruction %d, %d", outerIdx, startIdx) | ||||||
} | ||||||
|
||||||
commitment, err := postMessage.ConsistencyLevel.Commitment() | ||||||
|
@@ -328,7 +354,9 @@ func (s *SolanaWatcher) shimProcessRest( | |||||
Payload: postMessage.Payload, | ||||||
ConsistencyLevel: uint8(postMessage.ConsistencyLevel), | ||||||
IsReobservation: isReobservation, | ||||||
Unreliable: false, | ||||||
|
||||||
// Shim messages are always reliable. | ||||||
Unreliable: false, | ||||||
} | ||||||
|
||||||
solanaMessagesConfirmed.WithLabelValues(s.networkName).Inc() | ||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.