From 8bbb33ab70938619fe3534bce82a3def43a47069 Mon Sep 17 00:00:00 2001 From: Bruce Riley Date: Thu, 29 Aug 2024 17:40:12 -0500 Subject: [PATCH] Node/Solana: Refine search of log msgs for WH possible transactions --- node/pkg/watchers/solana/client.go | 31 +++-- node/pkg/watchers/solana/client_test.go | 155 ++++++++++++++++++++++++ 2 files changed, 177 insertions(+), 9 deletions(-) diff --git a/node/pkg/watchers/solana/client.go b/node/pkg/watchers/solana/client.go index e9dea6ab98..5613bda97b 100644 --- a/node/pkg/watchers/solana/client.go +++ b/node/pkg/watchers/solana/client.go @@ -242,7 +242,7 @@ func NewSolanaWatcher( wsUrl: wsUrl, contract: contractAddress, rawContract: rawContract, - whLogPrefix: fmt.Sprintf("Program %s", rawContract), + whLogPrefix: createWhLogPrefix(rawContract), msgObservedLogLevel: msgObservedLogLevel, msgC: msgC, obsvReqC: obsvReqC, @@ -593,13 +593,9 @@ func (s *SolanaWatcher) fetchBlock(ctx context.Context, logger *zap.Logger, slot continue } - // If the logs don't contain the contract address, skip the transaction. - // ex: "Program 3u8hJUVTA4jH1wYAyUur7FFZVQ8H635K3tSHHF4ssjQ5 invoke [2]", - var possiblyWormhole bool - for i := 0; i < len(txRpc.Meta.LogMessages) && !possiblyWormhole; i++ { - possiblyWormhole = strings.HasPrefix(txRpc.Meta.LogMessages[i], s.whLogPrefix) - } - if !possiblyWormhole { + // If the logs don't contain the contract address followed by a sequence number, skip the transaction. + // ex: "Program 3u8hJUVTA4jH1wYAyUur7FFZVQ8H635K3tSHHF4ssjQ5 invoke [2]", "Program log: Sequence: 937184". + if !isPossibleWormholeMessage(s.whLogPrefix, txRpc.Meta.LogMessages) { continue } @@ -668,6 +664,23 @@ func (s *SolanaWatcher) fetchBlock(ctx context.Context, logger *zap.Logger, slot return true } +func isPossibleWormholeMessage(whLogPrefix string, logMessages []string) bool { + for idx := 0; idx < len(logMessages); idx++ { + if strings.HasPrefix(logMessages[idx], whLogPrefix) { + for idx1 := idx + 1; idx1 < len(logMessages); idx1++ { + if strings.HasPrefix(logMessages[idx1], "Program log: Sequence:") { + return true + } + } + } + } + return false +} + +func createWhLogPrefix(rawContract string) string { + return fmt.Sprintf("Program %s invoke", rawContract) +} + func (s *SolanaWatcher) processParsedTransaction(ctx context.Context, logger *zap.Logger, parsedTxResult *rpc.GetParsedTransactionResult, signature solana.Signature, slot uint64, isReobservation bool) { foundContract := false for _, key := range parsedTxResult.Transaction.Message.AccountKeys { @@ -899,7 +912,7 @@ func (s *SolanaWatcher) processAccountSubscriptionData(_ context.Context, logger if value.Account.Owner != s.rawContract { // We got a message for the wrong contract on the websocket... uncomfortable... solanaConnectionErrors.WithLabelValues(s.networkName, string(s.commitment), "invalid_websocket_account").Inc() - return errors.New("Update for account with wrong owner") + return errors.New("update for account with wrong owner") } data, err = base64.StdEncoding.DecodeString(value.Account.Data[0]) diff --git a/node/pkg/watchers/solana/client_test.go b/node/pkg/watchers/solana/client_test.go index 15d41cae05..f961e11ade 100644 --- a/node/pkg/watchers/solana/client_test.go +++ b/node/pkg/watchers/solana/client_test.go @@ -13,3 +13,158 @@ func TestVerifyConstants(t *testing.T) { assert.Equal(t, SolanaAccountLen, solana.PublicKeyLength) assert.Equal(t, SolanaSignatureLen, len(solana.Signature{})) } + +var whLogPrefixForMainnet = createWhLogPrefix("worm2ZoG2kUd4vFXhvjh93UUH596ayRfgQ2MgjNMTth") +var whLogPrefixForTestnet = createWhLogPrefix("3u8hJUVTA4jH1wYAyUur7FFZVQ8H635K3tSHHF4ssjQ5") + +func TestIsPossibleWormholeMessageSuccess(t *testing.T) { + // These are actual logs see in mainnet on 8/29/2024. + logs := []string{ + "Program 3vxKRPwUTiEkeUVyoZ9MXFe1V71sRLbLqu1gRYaWmehQ invoke [1]", + "Program log: Instruction: TransferWrappedTokensWithRelay", + "Program 11111111111111111111111111111111 invoke [2]", + "Program 11111111111111111111111111111111 success", + "Program 11111111111111111111111111111111 invoke [2]", + "Program 11111111111111111111111111111111 success", + "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [2]", + "Program log: Instruction: InitializeAccount3", + "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 4214 of 189385 compute units", + "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success", + "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [2]", + "Program log: Instruction: Transfer", + "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 4645 of 162844 compute units", + "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success", + "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [2]", + "Program log: Instruction: Approve", + "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 2904 of 153570 compute units", + "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success", + "Program wormDTUJ6AWPNvk59vGQbDvGJmqbDTdgWgAqcLBCgUb invoke [2]", + "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [3]", + "Program log: Instruction: Burn", + "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 4790 of 91730 compute units", + "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success", + "Program 11111111111111111111111111111111 invoke [3]", + "Program 11111111111111111111111111111111 success", + "Program worm2ZoG2kUd4vFXhvjh93UUH596ayRfgQ2MgjNMTth invoke [3]", + "Program log: Sequence: 937184", + "Program 11111111111111111111111111111111 invoke [4]", + "Program 11111111111111111111111111111111 success", + "Program 11111111111111111111111111111111 invoke [4]", + "Program 11111111111111111111111111111111 success", + "Program 11111111111111111111111111111111 invoke [4]", + "Program 11111111111111111111111111111111 success", + "Program worm2ZoG2kUd4vFXhvjh93UUH596ayRfgQ2MgjNMTth consumed 27141 of 74067 compute units", + "Program worm2ZoG2kUd4vFXhvjh93UUH596ayRfgQ2MgjNMTth success", + "Program wormDTUJ6AWPNvk59vGQbDvGJmqbDTdgWgAqcLBCgUb consumed 87537 of 133116 compute units", + "Program wormDTUJ6AWPNvk59vGQbDvGJmqbDTdgWgAqcLBCgUb success", + "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [2]", + "Program log: Instruction: CloseAccount", + "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 3015 of 42346 compute units", + "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success", + "Program 3vxKRPwUTiEkeUVyoZ9MXFe1V71sRLbLqu1gRYaWmehQ consumed 186778 of 224134 compute units", + "Program 3vxKRPwUTiEkeUVyoZ9MXFe1V71sRLbLqu1gRYaWmehQ success", + "Program ComputeBudget111111111111111111111111111111 invoke [1]", + "Program ComputeBudget111111111111111111111111111111 success", + "Program ComputeBudget111111111111111111111111111111 invoke [1]", + "Program ComputeBudget111111111111111111111111111111 success", + } + + assert.True(t, isPossibleWormholeMessage(whLogPrefixForMainnet, logs)) +} + +func TestIsPossibleWormholeMessageFailNoLogs(t *testing.T) { + // These are actual logs see in mainnet on 8/29/2024. + logs := []string{} + + assert.False(t, isPossibleWormholeMessage(whLogPrefixForMainnet, logs)) +} + +func TestIsPossibleWormholeMessageFailNoWormhole(t *testing.T) { + // These are actual logs see in mainnet on 8/29/2024. + logs := []string{ + "Program 11111111111111111111111111111111 invoke [2]", + "Program 11111111111111111111111111111111 success", + "Program 11111111111111111111111111111111 invoke [2]", + "Program 11111111111111111111111111111111 success", + "Program 11111111111111111111111111111111 invoke [2]", + "Program 11111111111111111111111111111111 success", + "Program ComputeBudget111111111111111111111111111111 invoke [1]", + "Program ComputeBudget111111111111111111111111111111 success", + "Program ComputeBudget111111111111111111111111111111 invoke [1]", + "Program ComputeBudget111111111111111111111111111111 success", + } + + assert.False(t, isPossibleWormholeMessage(whLogPrefixForMainnet, logs)) +} + +func TestIsPossibleWormholeMessageFailNoSequence(t *testing.T) { + // These are actual logs see in mainnet on 8/29/2024. + logs := []string{ + "Program worm2ZoG2kUd4vFXhvjh93UUH596ayRfgQ2MgjNMTth invoke [1]", + "Program 11111111111111111111111111111111 invoke [2]", + "Program 11111111111111111111111111111111 success", + "Program 11111111111111111111111111111111 invoke [2]", + "Program 11111111111111111111111111111111 success", + "Program 11111111111111111111111111111111 invoke [2]", + "Program 11111111111111111111111111111111 success", + "Program worm2ZoG2kUd4vFXhvjh93UUH596ayRfgQ2MgjNMTth consumed 37058 of 500000 compute units", + "Program worm2ZoG2kUd4vFXhvjh93UUH596ayRfgQ2MgjNMTth success", + "Program ComputeBudget111111111111111111111111111111 invoke [1]", + "Program ComputeBudget111111111111111111111111111111 success", + "Program ComputeBudget111111111111111111111111111111 invoke [1]", + "Program ComputeBudget111111111111111111111111111111 success", + } + + assert.False(t, isPossibleWormholeMessage(whLogPrefixForMainnet, logs)) +} + +func TestIsPossibleWormholeMessageFailAtEnd(t *testing.T) { + // Note: I altered these logs to create this test. I don't know if this could ever happen. + logs := []string{ + "Program 11111111111111111111111111111111 invoke [2]", + "Program 11111111111111111111111111111111 success", + "Program 11111111111111111111111111111111 invoke [2]", + "Program 11111111111111111111111111111111 success", + "Program 11111111111111111111111111111111 invoke [2]", + "Program 11111111111111111111111111111111 success", + "Program worm2ZoG2kUd4vFXhvjh93UUH596ayRfgQ2MgjNMTth consumed 37058 of 500000 compute units", + "Program worm2ZoG2kUd4vFXhvjh93UUH596ayRfgQ2MgjNMTth success", + "Program ComputeBudget111111111111111111111111111111 invoke [1]", + "Program ComputeBudget111111111111111111111111111111 success", + "Program ComputeBudget111111111111111111111111111111 invoke [1]", + "Program ComputeBudget111111111111111111111111111111 success", + "Program worm2ZoG2kUd4vFXhvjh93UUH596ayRfgQ2MgjNMTth invoke [1]", + } + + assert.False(t, isPossibleWormholeMessage(whLogPrefixForMainnet, logs)) +} + +func TestIsPossibleWormholeMessageForSolanaRewriteSuccess(t *testing.T) { + // These are actual logs see in testnet on 8/30/2024. + logs := []string{ + "Program 4cmLyfxkgj2rGkPnXXJG8uroGvoSqsgigAsB3ACci2x7 invoke [1]", + "Program log: Instruction: WithdrawNative", + "Program 11111111111111111111111111111111 invoke [2]", + "Program 11111111111111111111111111111111 success", + "Program log: ctx.accounts.wormhole_program : 3u8hJUVTA4jH1wYAyUur7FFZVQ8H635K3tSHHF4ssjQ5", + "Program log: ctx.accounts.wormhole_bridge : 6bi4JGDoRwUs9TYBuvoA7dUVyikTJDrJsJU1ew6KVLiu", + "Program log: ctx.accounts.wormhole_message : DzKjuonZWoBjc9VcrtBBXwnEXA248Ma6fjUp1hgez9yg", + "Program log: ctx.accounts.custom_wormhole_emitter : F7thxxU2rLuXBj8xxzA39K63oZALrFuYRkCPUpQVhRb3", + "Program log: ctx.accounts.custom_wormhole_sequence : FEDW1vsFoGbQkaw9gbwuEZEsSwXLHcAKBVesXnmYNQnU", + "Program log: ctx.accounts.depositor : 3X1PnDdoyMdzLEoEy2TAhw8i1zC1F4ipNsxJHotnzCKK", + "Program log: ctx.accounts.wormhole_fee_collector : 7s3a1ycs16d6SNDumaRtjcoyMaTDZPavzgsmS3uUZYWX", + "Program 3u8hJUVTA4jH1wYAyUur7FFZVQ8H635K3tSHHF4ssjQ5 invoke [2]", + "Program log: Instruction: LegacyPostMessage", + "Program 11111111111111111111111111111111 invoke [3]", + "Program 11111111111111111111111111111111 success", + "Program 11111111111111111111111111111111 invoke [3]", + "Program 11111111111111111111111111111111 success", + "Program log: Sequence: 26", + "Program 3u8hJUVTA4jH1wYAyUur7FFZVQ8H635K3tSHHF4ssjQ5 consumed 36772 of 68261 compute units", + "Program 3u8hJUVTA4jH1wYAyUur7FFZVQ8H635K3tSHHF4ssjQ5 success", + "Program 4cmLyfxkgj2rGkPnXXJG8uroGvoSqsgigAsB3ACci2x7 consumed 169398 of 200000 compute units", + "Program 4cmLyfxkgj2rGkPnXXJG8uroGvoSqsgigAsB3ACci2x7 success", + } + + assert.True(t, isPossibleWormholeMessage(whLogPrefixForTestnet, logs)) +}