diff --git a/changelog.md b/changelog.md index 6731cec07b..58a7a69d9e 100644 --- a/changelog.md +++ b/changelog.md @@ -56,6 +56,7 @@ * [3914](https://github.com/zeta-chain/node/pull/3914) - check tx result err in filter inbound events * [3904](https://github.com/zeta-chain/node/pull/3904) - improve observer emissions distribution to maximise pool utilisation * [3895](https://github.com/zeta-chain/node/pull/3895) - solana call required accounts number condition +* [3896](https://github.com/zeta-chain/node/pull/3896) - add sender to solana execute message hash ### Tests diff --git a/contrib/localnet/solana/gateway.so b/contrib/localnet/solana/gateway.so index dd903d936b..c3a6dcfe13 100755 Binary files a/contrib/localnet/solana/gateway.so and b/contrib/localnet/solana/gateway.so differ diff --git a/pkg/contracts/solana/gateway_message.go b/pkg/contracts/solana/gateway_message.go index 052ddf547f..66fa9d21cb 100644 --- a/pkg/contracts/solana/gateway_message.go +++ b/pkg/contracts/solana/gateway_message.go @@ -339,6 +339,12 @@ func (msg *MsgExecute) Hash() [32]byte { message = append(message, msg.to.Bytes()...) + if msg.executeType == ExecuteTypeCall { + message = append(message, common.HexToAddress(msg.sender).Bytes()...) + } else { + message = append(message, solana.MustPublicKeyFromBase58(msg.sender).Bytes()...) + } + message = append(message, msg.data...) return crypto.Keccak256Hash(message) @@ -648,6 +654,12 @@ func (msg *MsgExecuteSPL) Hash() [32]byte { message = append(message, msg.recipientAta.Bytes()...) + if msg.executeType == ExecuteTypeCall { + message = append(message, common.HexToAddress(msg.sender).Bytes()...) + } else { + message = append(message, solana.MustPublicKeyFromBase58(msg.sender).Bytes()...) + } + message = append(message, msg.data...) return crypto.Keccak256Hash(message) diff --git a/pkg/contracts/solana/gateway_message_test.go b/pkg/contracts/solana/gateway_message_test.go index b86eebc3a3..e3fa5de907 100644 --- a/pkg/contracts/solana/gateway_message_test.go +++ b/pkg/contracts/solana/gateway_message_test.go @@ -108,7 +108,7 @@ func Test_MsgExecuteHash(t *testing.T) { to := solana.MustPublicKeyFromBase58("37yGiHAnLvWZUNVwu9esp74YQFqxU1qHCbABkDvRddUQ") sender := common.HexToAddress("0x42bd6E2ce4CDb2F58Ed0A0E427F011A0645D5E33") - wantHash := "7391cf357fd80e7cb3d2a9758932fdea4988d03c87210a2632f03b467728d199" + wantHash := "ff0737262c010614dead18a4ed152ca38bd92aae694f08ba611cea307f3d92d9" wantHashBytes := testutil.HexToBytes(t, wantHash) // ACT @@ -134,7 +134,7 @@ func Test_MsgExecuteSPLHash(t *testing.T) { require.NoError(t, err) sender := common.HexToAddress("0x42bd6E2ce4CDb2F58Ed0A0E427F011A0645D5E33") - wantHash := "d90f9640faecd76509b4e88fa7d18f130918130b8666179f1597f185a828d3a5" + wantHash := "8ed9d12294399703611bf6b4d131aa0cdd9b1c2ca7e2586238c47929766ed0b9" wantHashBytes := testutil.HexToBytes(t, wantHash) // ACT @@ -155,14 +155,14 @@ func Test_MsgExecuteRevertHash(t *testing.T) { nonce := uint64(0) amount := uint64(1336000) to := solana.MustPublicKeyFromBase58("37yGiHAnLvWZUNVwu9esp74YQFqxU1qHCbABkDvRddUQ") - sender := common.HexToAddress("0x42bd6E2ce4CDb2F58Ed0A0E427F011A0645D5E33") + sender := solana.MustPublicKeyFromBase58("CVoPuE3EMu6QptGHLx7mDGb2ZgASJRQ5BcTvmhZNJd8A") - wantHash := "A55A5E8E302D5BA9A4C2DCDE54225F45F5E20081873AA7F6A3A361DB20527E31" + wantHash := "8a84b88736c9b677b1d859b212bd07f9808f8ca07682b3585eb1b6c5f2f6f4dd" wantHashBytes := testutil.HexToBytes(t, wantHash) // ACT // create new execute message - hash := contracts.NewMsgExecute(chainID, nonce, amount, to, sender.Hex(), []byte("hello"), contracts.ExecuteTypeRevert, []*solana.AccountMeta{}). + hash := contracts.NewMsgExecute(chainID, nonce, amount, to, sender.String(), []byte("hello"), contracts.ExecuteTypeRevert, []*solana.AccountMeta{}). Hash() // ASSERT @@ -180,14 +180,14 @@ func Test_MsgExecuteSPLRevertHash(t *testing.T) { to := solana.MustPublicKeyFromBase58("37yGiHAnLvWZUNVwu9esp74YQFqxU1qHCbABkDvRddUQ") toAta, _, err := solana.FindAssociatedTokenAddress(to, mintAccount) require.NoError(t, err) - sender := common.HexToAddress("0x42bd6E2ce4CDb2F58Ed0A0E427F011A0645D5E33") + sender := solana.MustPublicKeyFromBase58("CVoPuE3EMu6QptGHLx7mDGb2ZgASJRQ5BcTvmhZNJd8A") - wantHash := "C27871066FB9F28387E39281F267688FC54CE584F321891454503B17C3353B11" + wantHash := "147f471f794e0227653f69c40d9ac0ab278ac8549de41d9229e9b6ca14735c57" wantHashBytes := testutil.HexToBytes(t, wantHash) // ACT // create new execute message - hash := contracts.NewMsgExecuteSPL(chainID, nonce, amount, 8, mintAccount, to, toAta, sender.Hex(), []byte("hello"), contracts.ExecuteTypeRevert, []*solana.AccountMeta{}). + hash := contracts.NewMsgExecuteSPL(chainID, nonce, amount, 8, mintAccount, to, toAta, sender.String(), []byte("hello"), contracts.ExecuteTypeRevert, []*solana.AccountMeta{}). Hash() // ASSERT diff --git a/zetaclient/chains/solana/signer/execute.go b/zetaclient/chains/solana/signer/execute.go index a30959f4c9..a4db6e249d 100644 --- a/zetaclient/chains/solana/signer/execute.go +++ b/zetaclient/chains/solana/signer/execute.go @@ -2,6 +2,7 @@ package signer import ( "context" + "fmt" "cosmossdk.io/errors" "github.com/ethereum/go-ethereum/common" @@ -74,6 +75,12 @@ func (signer *Signer) createMsgExecute( return nil, nil, errors.Wrapf(err, "cannot decode receiver address %s", params.Receiver) } + // check sender based on execute type + sender, err := validateSender(cctx.InboundParams.Sender, executeType) + if err != nil { + return nil, nil, errors.Wrap(err, "cannot validate sender") + } + remainingAccounts := []*solana.AccountMeta{} for _, a := range msg.Accounts { remainingAccounts = append(remainingAccounts, &solana.AccountMeta{ @@ -87,7 +94,7 @@ func (signer *Signer) createMsgExecute( nonce, amount, to, - cctx.InboundParams.Sender, + sender, msg.Data, executeType, remainingAccounts, @@ -158,3 +165,22 @@ func (signer *Signer) createExecuteInstruction(msg contracts.MsgExecute) (*solan return inst, nil } + +// validateSender validates and formats the sender address based on execute type +func validateSender(sender string, executeType contracts.ExecuteType) (string, error) { + if executeType == contracts.ExecuteTypeCall { + // for regular execute, sender should be an Ethereum address + senderEth := common.HexToAddress(sender) + if senderEth == (common.Address{}) { + return "", fmt.Errorf("invalid execute sender %s", sender) + } + return senderEth.Hex(), nil + } + + // for revert execute, sender should be a Solana address + senderSol, err := solana.PublicKeyFromBase58(sender) + if err != nil { + return "", errors.Wrapf(err, "invalid execute revert sender %s", sender) + } + return senderSol.String(), nil +} diff --git a/zetaclient/chains/solana/signer/execute_spl.go b/zetaclient/chains/solana/signer/execute_spl.go index a80ba3a522..0e4d176eb5 100644 --- a/zetaclient/chains/solana/signer/execute_spl.go +++ b/zetaclient/chains/solana/signer/execute_spl.go @@ -80,6 +80,12 @@ func (signer *Signer) createMsgExecuteSPL( return nil, nil, errors.Wrapf(err, "cannot decode receiver address %s", params.Receiver) } + // check sender based on execute type + sender, err := validateSender(cctx.InboundParams.Sender, executeType) + if err != nil { + return nil, nil, errors.Wrap(err, "cannot validate sender") + } + // parse mint account mintAccount, err := solana.PublicKeyFromBase58(cctx.InboundParams.Asset) if err != nil { @@ -119,7 +125,7 @@ func (signer *Signer) createMsgExecuteSPL( mintAccount, to, destinationProgramPdaAta, - cctx.InboundParams.Sender, + sender, msg.Data, executeType, remainingAccounts, diff --git a/zetaclient/chains/solana/signer/withdraw_spl.go b/zetaclient/chains/solana/signer/withdraw_spl.go index b1f33c6e45..bd83b8ee24 100644 --- a/zetaclient/chains/solana/signer/withdraw_spl.go +++ b/zetaclient/chains/solana/signer/withdraw_spl.go @@ -143,7 +143,12 @@ func (signer *Signer) createWithdrawSPLInstruction(msg contracts.MsgWithdrawSPL) } func (signer *Signer) decodeMintAccountDetails(ctx context.Context, asset string) (token.Mint, error) { - info, err := signer.client.GetAccountInfo(ctx, solana.MustPublicKeyFromBase58(asset)) + mintPk, err := solana.PublicKeyFromBase58(asset) + if err != nil { + return token.Mint{}, err + } + + info, err := signer.client.GetAccountInfo(ctx, mintPk) if err != nil { return token.Mint{}, err }