From 0fc857c9e4ad6e5ee6980bd5670f2de29ae0e694 Mon Sep 17 00:00:00 2001 From: skosito Date: Wed, 9 Oct 2024 03:31:49 +0200 Subject: [PATCH 01/20] whitelist spl mint wip --- cmd/zetae2e/local/solana.go | 1 + contrib/localnet/solana/gateway-keypair.json | 2 +- contrib/localnet/solana/gateway.so | Bin 278648 -> 326472 bytes e2e/e2etests/e2etests.go | 8 +- e2e/e2etests/test_solana_whitelist_spl.go | 55 ++ e2e/runner/setup_solana.go | 2 +- e2e/runner/solana.go | 39 +- pkg/contracts/solana/gateway.go | 7 +- pkg/contracts/solana/gateway.json | 528 +++++++++++++++++- pkg/contracts/solana/gateway_message.go | 16 + pkg/contracts/solana/instruction.go | 24 + .../keeper/msg_server_whitelist_erc20.go | 45 +- x/crosschain/types/message_whitelist_erc20.go | 7 +- zetaclient/chains/solana/signer/signer.go | 45 ++ zetaclient/chains/solana/signer/whitelist.go | 81 +++ zetaclient/testutils/constant.go | 2 +- 16 files changed, 822 insertions(+), 40 deletions(-) create mode 100644 e2e/e2etests/test_solana_whitelist_spl.go create mode 100644 zetaclient/chains/solana/signer/whitelist.go diff --git a/cmd/zetae2e/local/solana.go b/cmd/zetae2e/local/solana.go index 135615051a..3e58418cc4 100644 --- a/cmd/zetae2e/local/solana.go +++ b/cmd/zetae2e/local/solana.go @@ -26,6 +26,7 @@ func solanaTestRoutine( deployerRunner, conf.AdditionalAccounts.UserSolana, runner.NewLogger(verbose, color.FgCyan, "solana"), + runner.WithZetaTxServer(deployerRunner.ZetaTxServer), ) if err != nil { return err diff --git a/contrib/localnet/solana/gateway-keypair.json b/contrib/localnet/solana/gateway-keypair.json index 99c8b61dee..daab863409 100644 --- a/contrib/localnet/solana/gateway-keypair.json +++ b/contrib/localnet/solana/gateway-keypair.json @@ -1 +1 @@ -[148,138,110,3,169,253,42,101,79,110,149,110,112,214,41,163,75,28,36,29,241,151,41,200,135,185,252,180,158,191,166,156,119,192,217,18,69,149,119,145,212,43,144,149,176,111,89,140,102,63,193,127,241,148,51,161,170,62,19,196,239,253,6,192] \ No newline at end of file +[170,171,179,164,93,78,137,61,23,64,233,109,110,253,36,57,111,127,55,106,199,85,39,175,73,206,30,244,229,216,101,145,157,23,123,51,143,17,39,82,72,82,114,55,41,2,101,170,230,6,41,179,227,225,43,107,236,186,181,51,118,110,43,227] \ No newline at end of file diff --git a/contrib/localnet/solana/gateway.so b/contrib/localnet/solana/gateway.so index 185b938c3ca936bfc13c831b38af4b826e8bef8a..3f991179619d09e68a922f3612fc85d0aa0ae666 100755 GIT binary patch literal 326472 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

E5GXi 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 Q)Yy!yhoEI5fAIMK4_fJMd;kCd delta 73092 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 LNLOaMw!{Au+Mxbo diff --git a/e2e/e2etests/e2etests.go b/e2e/e2etests/e2etests.go index 004271c696..4a29ae30c4 100644 --- a/e2e/e2etests/e2etests.go +++ b/e2e/e2etests/e2etests.go @@ -61,7 +61,7 @@ const ( TestSolanaDepositAndCallRefundName = "solana_deposit_and_call_refund" TestSolanaDepositRestrictedName = "solana_deposit_restricted" TestSolanaWithdrawRestrictedName = "solana_withdraw_restricted" - + TestSolanaWhitelistSPLName = "solana_whitelist_spl" /** * TON tests */ @@ -438,6 +438,12 @@ var AllE2ETests = []runner.E2ETest{ }, TestSolanaWithdrawRestricted, ), + runner.NewE2ETest( + TestSolanaWhitelistSPLName, + "whitelist SPL", + []runner.ArgDefinition{}, + TestSolanaWhitelistSPL, + ), /* TON tests */ diff --git a/e2e/e2etests/test_solana_whitelist_spl.go b/e2e/e2etests/test_solana_whitelist_spl.go new file mode 100644 index 0000000000..5276d78552 --- /dev/null +++ b/e2e/e2etests/test_solana_whitelist_spl.go @@ -0,0 +1,55 @@ +package e2etests + +import ( + "github.com/gagliardetto/solana-go" + "github.com/stretchr/testify/require" + "github.com/zeta-chain/node/e2e/runner" + "github.com/zeta-chain/node/e2e/txserver" + "github.com/zeta-chain/node/e2e/utils" + "github.com/zeta-chain/node/pkg/chains" + crosschaintypes "github.com/zeta-chain/node/x/crosschain/types" +) + +func TestSolanaWhitelistSPL(r *runner.E2ERunner, args []string) { + // Deploy a new SPL + r.Logger.Info("Deploying new SPL") + + // load deployer private key + privkey, err := solana.PrivateKeyFromBase58(r.Account.SolanaPrivateKey.String()) + require.NoError(r, err) + + spl := r.DeploySPL(&privkey) + + // whitelist erc20 zrc20 + r.Logger.Info("whitelisting ERC20 on new network") + res, err := r.ZetaTxServer.BroadcastTx(utils.AdminPolicyName, crosschaintypes.NewMsgWhitelistERC20( + r.ZetaTxServer.MustGetAccountAddressFromName(utils.AdminPolicyName), + spl.PublicKey().String(), + chains.SolanaLocalnet.ChainId, + "NEWSOLERC20", + "NEWSOLERC20", + 6, + 100000, + )) + require.NoError(r, err) + + // retrieve zrc20 and cctx from event + whitelistCCTXIndex, err := txserver.FetchAttributeFromTxResponse(res, "whitelist_cctx_index") + require.NoError(r, err) + + erc20zrc20Addr, err := txserver.FetchAttributeFromTxResponse(res, "zrc20_address") + require.NoError(r, err) + + err = r.ZetaTxServer.InitializeLiquidityCap(erc20zrc20Addr) + require.NoError(r, err) + + // ensure CCTX created + resCCTX, err := r.CctxClient.Cctx(r.Ctx, &crosschaintypes.QueryGetCctxRequest{Index: whitelistCCTXIndex}) + require.NoError(r, err) + + cctx := resCCTX.CrossChainTx + r.Logger.CCTX(*cctx, "whitelist_cctx") + + // wait for the whitelist cctx to be mined + r.WaitForMinedCCTXFromIndex(whitelistCCTXIndex) +} diff --git a/e2e/runner/setup_solana.go b/e2e/runner/setup_solana.go index 8f209dd23e..d5c4bb20de 100644 --- a/e2e/runner/setup_solana.go +++ b/e2e/runner/setup_solana.go @@ -55,7 +55,7 @@ func (r *E2ERunner) SetSolanaContracts(deployerPrivateKey string) { require.NoError(r, err) // create and sign the transaction - signedTx := r.CreateSignedTransaction([]solana.Instruction{&inst}, privkey) + signedTx := r.CreateSignedTransaction([]solana.Instruction{&inst}, privkey, []solana.PrivateKey{}) // broadcast the transaction and wait for finalization _, out := r.BroadcastTxSync(signedTx) diff --git a/e2e/runner/solana.go b/e2e/runner/solana.go index 8ff67b9d72..e4ed5162df 100644 --- a/e2e/runner/solana.go +++ b/e2e/runner/solana.go @@ -6,6 +6,8 @@ import ( ethcommon "github.com/ethereum/go-ethereum/common" "github.com/gagliardetto/solana-go" + "github.com/gagliardetto/solana-go/programs/system" + "github.com/gagliardetto/solana-go/programs/token" "github.com/gagliardetto/solana-go/rpc" "github.com/near/borsh-go" "github.com/stretchr/testify/require" @@ -62,6 +64,7 @@ func (r *E2ERunner) CreateDepositInstruction( func (r *E2ERunner) CreateSignedTransaction( instructions []solana.Instruction, privateKey solana.PrivateKey, + additionalPrivateKeys []solana.PrivateKey, ) *solana.Transaction { // get a recent blockhash recent, err := r.SolanaClient.GetLatestBlockhash(r.Ctx, rpc.CommitmentFinalized) @@ -81,6 +84,11 @@ func (r *E2ERunner) CreateSignedTransaction( if privateKey.PublicKey().Equals(key) { return &privateKey } + for _, apk := range additionalPrivateKeys { + if apk.PublicKey().Equals(key) { + return &apk + } + } return nil }, ) @@ -89,6 +97,35 @@ func (r *E2ERunner) CreateSignedTransaction( return tx } +func (r *E2ERunner) DeploySPL(privateKey *solana.PrivateKey) *solana.Wallet { + lamport, err := r.SolanaClient.GetMinimumBalanceForRentExemption(r.Ctx, token.MINT_SIZE, rpc.CommitmentFinalized) + require.NoError(r, err) + + tokenAccount := solana.NewWallet() + createAccountInstruction := system.NewCreateAccountInstruction( + lamport, + token.MINT_SIZE, + solana.TokenProgramID, + privateKey.PublicKey(), + tokenAccount.PublicKey(), + ).Build() + + initializeMintInstruction := token.NewInitializeMint2Instruction( + 6, + privateKey.PublicKey(), + privateKey.PublicKey(), + tokenAccount.PublicKey(), + ).Build() + + signedTx := r.CreateSignedTransaction([]solana.Instruction{createAccountInstruction, initializeMintInstruction}, *privateKey, []solana.PrivateKey{tokenAccount.PrivateKey}) + + // broadcast the transaction and wait for finalization + _, out := r.BroadcastTxSync(signedTx) + r.Logger.Info("create spl logs: %v", out.Meta.LogMessages) + + return tokenAccount +} + // BroadcastTxSync broadcasts a transaction and waits for it to be finalized func (r *E2ERunner) BroadcastTxSync(tx *solana.Transaction) (solana.Signature, *rpc.GetTransactionResult) { // broadcast the transaction @@ -134,7 +171,7 @@ func (r *E2ERunner) SOLDepositAndCall( instruction := r.CreateDepositInstruction(signerPrivKey.PublicKey(), receiver, data, amount.Uint64()) // create and sign the transaction - signedTx := r.CreateSignedTransaction([]solana.Instruction{instruction}, *signerPrivKey) + signedTx := r.CreateSignedTransaction([]solana.Instruction{instruction}, *signerPrivKey, []solana.PrivateKey{}) // broadcast the transaction and wait for finalization sig, out := r.BroadcastTxSync(signedTx) diff --git a/pkg/contracts/solana/gateway.go b/pkg/contracts/solana/gateway.go index a8f0c571e5..5049a3c668 100644 --- a/pkg/contracts/solana/gateway.go +++ b/pkg/contracts/solana/gateway.go @@ -8,7 +8,7 @@ import ( const ( // SolanaGatewayProgramID is the program ID of the Solana gateway program - SolanaGatewayProgramID = "94U5AHQMKkV5txNJ17QPXWoh474PheGou6cNP2FEuL1d" + SolanaGatewayProgramID = "BaDmykPHVwPQNY9SXQnJU8JPXdN89z3ib7qEfhNfkWRg" // PDASeed is the seed for the Solana gateway program derived address PDASeed = "meta" @@ -43,6 +43,11 @@ func DiscriminatorWithdrawSPL() [8]byte { return [8]byte{156, 234, 11, 89, 235, 246, 32} } +// DiscriminatorWhitelist returns the discriminator for Solana gateway 'whitelist_spl_mint' instruction +func DiscriminatorWhitelistSplMint() [8]byte { + return [8]byte{30, 110, 162, 42, 208, 147, 254, 219} +} + // ParseGatewayAddressAndPda parses the gateway id and program derived address from the given string func ParseGatewayIDAndPda(address string) (solana.PublicKey, solana.PublicKey, error) { var gatewayID, pda solana.PublicKey diff --git a/pkg/contracts/solana/gateway.json b/pkg/contracts/solana/gateway.json index 8747c2ca0f..e183391604 100644 --- a/pkg/contracts/solana/gateway.json +++ b/pkg/contracts/solana/gateway.json @@ -1,5 +1,5 @@ { - "address": "94U5AHQMKkV5txNJ17QPXWoh474PheGou6cNP2FEuL1d", + "address": "BaDmykPHVwPQNY9SXQnJU8JPXdN89z3ib7qEfhNfkWRg", "metadata": { "name": "gateway", "version": "0.1.0", @@ -27,7 +27,76 @@ }, { "name": "pda", - "writable": true + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 109, + 101, + 116, + 97 + ] + } + ] + } + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + } + ], + "args": [ + { + "name": "amount", + "type": "u64" + }, + { + "name": "receiver", + "type": { + "array": [ + "u8", + 20 + ] + } + } + ] + }, + { + "name": "deposit_and_call", + "discriminator": [ + 65, + 33, + 186, + 198, + 114, + 223, + 133, + 57 + ], + "accounts": [ + { + "name": "signer", + "writable": true, + "signer": true + }, + { + "name": "pda", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 109, + 101, + 116, + 97 + ] + } + ] + } }, { "name": "system_program", @@ -40,7 +109,16 @@ "type": "u64" }, { - "name": "memo", + "name": "receiver", + "type": { + "array": [ + "u8", + 20 + ] + } + }, + { + "name": "message", "type": "bytes" } ] @@ -65,7 +143,97 @@ }, { "name": "pda", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 109, + 101, + 116, + 97 + ] + } + ] + } + }, + { + "name": "whitelist_entry", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 119, + 104, + 105, + 116, + 101, + 108, + 105, + 115, + 116 + ] + }, + { + "kind": "account", + "path": "mint_account" + } + ] + } + }, + { + "name": "mint_account" + }, + { + "name": "token_program", + "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" + }, + { + "name": "from", + "writable": true + }, + { + "name": "to", + "writable": true + } + ], + "args": [ + { + "name": "amount", + "type": "u64" + }, + { + "name": "receiver", + "type": { + "array": [ + "u8", + 20 + ] + } + } + ] + }, + { + "name": "deposit_spl_token_and_call", + "discriminator": [ + 14, + 181, + 27, + 187, + 171, + 61, + 237, + 147 + ], + "accounts": [ + { + "name": "signer", "writable": true, + "signer": true + }, + { + "name": "pda", "pda": { "seeds": [ { @@ -80,6 +248,34 @@ ] } }, + { + "name": "whitelist_entry", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 119, + 104, + 105, + 116, + 101, + 108, + 105, + 115, + 116 + ] + }, + { + "kind": "account", + "path": "mint_account" + } + ] + } + }, + { + "name": "mint_account" + }, { "name": "token_program", "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" @@ -99,7 +295,16 @@ "type": "u64" }, { - "name": "memo", + "name": "receiver", + "type": { + "array": [ + "u8", + 20 + ] + } + }, + { + "name": "message", "type": "bytes" } ] @@ -153,6 +358,170 @@ 20 ] } + }, + { + "name": "chain_id", + "type": "u64" + } + ] + }, + { + "name": "set_deposit_paused", + "discriminator": [ + 98, + 179, + 141, + 24, + 246, + 120, + 164, + 143 + ], + "accounts": [ + { + "name": "pda", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 109, + 101, + 116, + 97 + ] + } + ] + } + }, + { + "name": "signer", + "writable": true, + "signer": true + } + ], + "args": [ + { + "name": "deposit_paused", + "type": "bool" + } + ] + }, + { + "name": "unwhitelist_spl_mint", + "discriminator": [ + 73, + 142, + 63, + 191, + 233, + 238, + 170, + 104 + ], + "accounts": [ + { + "name": "whitelist_entry", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 119, + 104, + 105, + 116, + 101, + 108, + 105, + 115, + 116 + ] + }, + { + "kind": "account", + "path": "whitelist_candidate" + } + ] + } + }, + { + "name": "whitelist_candidate" + }, + { + "name": "pda", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 109, + 101, + 116, + 97 + ] + } + ] + } + }, + { + "name": "authority", + "writable": true, + "signer": true, + "relations": [ + "pda" + ] + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + } + ], + "args": [] + }, + { + "name": "update_authority", + "discriminator": [ + 32, + 46, + 64, + 28, + 149, + 75, + 243, + 88 + ], + "accounts": [ + { + "name": "pda", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 109, + 101, + 116, + 97 + ] + } + ] + } + }, + { + "name": "signer", + "writable": true, + "signer": true + } + ], + "args": [ + { + "name": "new_authority_address", + "type": "pubkey" } ] }, @@ -171,7 +540,20 @@ "accounts": [ { "name": "pda", - "writable": true + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 109, + 101, + 116, + 97 + ] + } + ] + } }, { "name": "signer", @@ -191,6 +573,80 @@ } ] }, + { + "name": "whitelist_spl_mint", + "discriminator": [ + 30, + 110, + 162, + 42, + 208, + 147, + 254, + 219 + ], + "accounts": [ + { + "name": "whitelist_entry", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 119, + 104, + 105, + 116, + 101, + 108, + 105, + 115, + 116 + ] + }, + { + "kind": "account", + "path": "whitelist_candidate" + } + ] + } + }, + { + "name": "whitelist_candidate" + }, + { + "name": "pda", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 109, + 101, + 116, + 97 + ] + } + ] + } + }, + { + "name": "authority", + "writable": true, + "signer": true, + "relations": [ + "pda" + ] + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + } + ], + "args": [] + }, { "name": "withdraw", "discriminator": [ @@ -211,7 +667,20 @@ }, { "name": "pda", - "writable": true + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 109, + 101, + 116, + 97 + ] + } + ] + } }, { "name": "to", @@ -287,9 +756,12 @@ } }, { - "name": "from", + "name": "pda_ata", "writable": true }, + { + "name": "mint_account" + }, { "name": "to", "writable": true @@ -300,6 +772,10 @@ } ], "args": [ + { + "name": "decimals", + "type": "u8" + }, { "name": "amount", "type": "u64" @@ -346,6 +822,19 @@ 43, 94 ] + }, + { + "name": "WhitelistEntry", + "discriminator": [ + 51, + 70, + 173, + 81, + 219, + 192, + 234, + 62 + ] } ], "errors": [ @@ -388,6 +877,16 @@ "code": 6007, "name": "MemoLengthTooShort", "msg": "MemoLengthTooShort" + }, + { + "code": 6008, + "name": "DepositPaused", + "msg": "DepositPaused" + }, + { + "code": 6009, + "name": "SPLAtaAndMintAddressMismatch", + "msg": "SPLAtaAndMintAddressMismatch" } ], "types": [ @@ -412,9 +911,24 @@ { "name": "authority", "type": "pubkey" + }, + { + "name": "chain_id", + "type": "u64" + }, + { + "name": "deposit_paused", + "type": "bool" } ] } + }, + { + "name": "WhitelistEntry", + "type": { + "kind": "struct", + "fields": [] + } } ] } \ No newline at end of file diff --git a/pkg/contracts/solana/gateway_message.go b/pkg/contracts/solana/gateway_message.go index 021af3cf1f..1f31e1d63b 100644 --- a/pkg/contracts/solana/gateway_message.go +++ b/pkg/contracts/solana/gateway_message.go @@ -105,3 +105,19 @@ func (msg *MsgWithdraw) Signer() (common.Address, error) { return RecoverSigner(msgHash[:], msgSig[:]) } + +// MsgWhitelist is the message for the Solana gateway whitelist_spl_mint instruction +type MsgWhitelist struct { + // whitelistCandidate is the whitelist candidate + whitelistCandidate solana.PublicKey +} + +// NewMsgWhitelist returns a new whitelist_spl_mint message +func NewMsgWhitelist(whitelistCandidate solana.PublicKey) *MsgWhitelist { + return &MsgWhitelist{whitelistCandidate: whitelistCandidate} +} + +// To returns the recipient address of the message +func (msg *MsgWhitelist) WhitelistCandidate() solana.PublicKey { + return msg.whitelistCandidate +} diff --git a/pkg/contracts/solana/instruction.go b/pkg/contracts/solana/instruction.go index f338129c9b..f69e7e8079 100644 --- a/pkg/contracts/solana/instruction.go +++ b/pkg/contracts/solana/instruction.go @@ -116,3 +116,27 @@ func RecoverSigner(msgHash []byte, msgSig []byte) (signer common.Address, err er return crypto.PubkeyToAddress(*pubKey), nil } + +// WhitelistInstructionParams contains the parameters for a gateway whitelist_spl_mint instruction +type WhitelistInstructionParams struct { + // Discriminator is the unique identifier for the whitelist_spl_mint instruction + Discriminator [8]byte +} + +// ParseInstructionWhitelist tries to parse the instruction as a 'whitelist_spl_mint'. +// It returns nil if the instruction can't be parsed as a 'whitelist_spl_mint'. +func ParseInstructionWhitelist(instruction solana.CompiledInstruction) (*WhitelistInstructionParams, error) { + // try deserializing instruction as a 'whitelist_spl_mint' + inst := &WhitelistInstructionParams{} + err := borsh.Deserialize(inst, instruction.Data) + if err != nil { + return nil, errors.Wrap(err, "error deserializing instruction") + } + + // check the discriminator to ensure it's a 'whitelist_spl_mint' instruction + if inst.Discriminator != DiscriminatorWhitelistSplMint() { + return nil, fmt.Errorf("not a whitelist_spl_mint instruction: %v", inst.Discriminator) + } + + return inst, nil +} diff --git a/x/crosschain/keeper/msg_server_whitelist_erc20.go b/x/crosschain/keeper/msg_server_whitelist_erc20.go index 4ae98a85b5..7321fdb835 100644 --- a/x/crosschain/keeper/msg_server_whitelist_erc20.go +++ b/x/crosschain/keeper/msg_server_whitelist_erc20.go @@ -6,7 +6,6 @@ import ( errorsmod "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ethcommon "github.com/ethereum/go-ethereum/common" "github.com/zeta-chain/node/pkg/coin" @@ -31,28 +30,28 @@ func (k msgServer) WhitelistERC20( return nil, errorsmod.Wrap(authoritytypes.ErrUnauthorized, err.Error()) } - erc20Addr := ethcommon.HexToAddress(msg.Erc20Address) - if erc20Addr == (ethcommon.Address{}) { - return nil, errorsmod.Wrapf( - sdkerrors.ErrInvalidAddress, - "invalid ERC20 contract address (%s)", - msg.Erc20Address, - ) - } - - // check if the erc20 is already whitelisted - foreignCoins := k.fungibleKeeper.GetAllForeignCoins(ctx) - for _, fCoin := range foreignCoins { - assetAddr := ethcommon.HexToAddress(fCoin.Asset) - if assetAddr == erc20Addr && fCoin.ForeignChainId == msg.ChainId { - return nil, errorsmod.Wrapf( - fungibletypes.ErrForeignCoinAlreadyExist, - "ERC20 contract address (%s) already whitelisted on chain (%d)", - msg.Erc20Address, - msg.ChainId, - ) - } - } + // erc20Addr := ethcommon.HexToAddress(msg.Erc20Address) + // if erc20Addr == (ethcommon.Address{}) { + // return nil, errorsmod.Wrapf( + // sdkerrors.ErrInvalidAddress, + // "invalid ERC20 contract address (%s)", + // msg.Erc20Address, + // ) + // } + + // // check if the erc20 is already whitelisted + // foreignCoins := k.fungibleKeeper.GetAllForeignCoins(ctx) + // for _, fCoin := range foreignCoins { + // assetAddr := ethcommon.HexToAddress(fCoin.Asset) + // if assetAddr == erc20Addr && fCoin.ForeignChainId == msg.ChainId { + // return nil, errorsmod.Wrapf( + // fungibletypes.ErrForeignCoinAlreadyExist, + // "ERC20 contract address (%s) already whitelisted on chain (%d)", + // msg.Erc20Address, + // msg.ChainId, + // ) + // } + // } tss, found := k.zetaObserverKeeper.GetTSS(ctx) if !found { diff --git a/x/crosschain/types/message_whitelist_erc20.go b/x/crosschain/types/message_whitelist_erc20.go index 3267581662..4620e4be08 100644 --- a/x/crosschain/types/message_whitelist_erc20.go +++ b/x/crosschain/types/message_whitelist_erc20.go @@ -4,7 +4,6 @@ import ( cosmoserrors "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - ethcommon "github.com/ethereum/go-ethereum/common" "github.com/zeta-chain/node/x/fungible/types" ) @@ -54,9 +53,9 @@ func (msg *MsgWhitelistERC20) ValidateBasic() error { return cosmoserrors.Wrapf(sdkerrors.ErrInvalidAddress, "invalid creator address (%s)", err) } // check if the system contract address is valid - if ethcommon.HexToAddress(msg.Erc20Address) == (ethcommon.Address{}) { - return cosmoserrors.Wrapf(sdkerrors.ErrInvalidAddress, "invalid ERC20 contract address (%s)", msg.Erc20Address) - } + // if ethcommon.HexToAddress(msg.Erc20Address) == (ethcommon.Address{}) { + // return cosmoserrors.Wrapf(sdkerrors.ErrInvalidAddress, "invalid ERC20 contract address (%s)", msg.Erc20Address) + // } if msg.Decimals > 128 { return cosmoserrors.Wrapf(types.ErrInvalidDecimals, "invalid decimals (%d)", msg.Decimals) } diff --git a/zetaclient/chains/solana/signer/signer.go b/zetaclient/chains/solana/signer/signer.go index 3b64a1ff7c..a77f456c32 100644 --- a/zetaclient/chains/solana/signer/signer.go +++ b/zetaclient/chains/solana/signer/signer.go @@ -2,6 +2,8 @@ package signer import ( "context" + "fmt" + "strings" "cosmossdk.io/errors" ethcommon "github.com/ethereum/go-ethereum/common" @@ -121,6 +123,49 @@ func (signer *Signer) TryProcessOutbound( chainID := signer.Chain().ChainId nonce := params.TssNonce coinType := cctx.InboundParams.CoinType + relayedMsg := strings.Split(cctx.RelayedMessage, ":") + if len(relayedMsg) != 2 { + return + } + fmt.Println("debug ", coinType, relayedMsg[1]) + + if coinType == coin.CoinType_Cmd { + pk, err := solana.PublicKeyFromBase58(relayedMsg[1]) + if err != nil { + fmt.Println("err decoding ", err.Error()) + } + + // sign the withdraw transaction by relayer key + tx, err := signer.SignWhitelistTx(ctx, contracts.NewMsgWhitelist(pk)) + if err != nil { + logger.Error().Err(err).Msgf("TryProcessOutbound: SignGasWithdraw error for chain %d nonce %d", chainID, nonce) + return + } + + // broadcast the signed tx to the Solana network with preflight check + txSig, err := signer.client.SendTransactionWithOpts( + ctx, + tx, + // Commitment "finalized" is too conservative for preflight check and + // it results in repeated broadcast attempts that only 1 will succeed. + // Commitment "processed" will simulate tx against more recent state + // thus fails faster once a tx is already broadcasted and processed by the cluster. + // This reduces the number of "failed" txs due to repeated broadcast attempts. + rpc.TransactionOpts{PreflightCommitment: rpc.CommitmentProcessed}, + ) + if err != nil { + signer.Logger(). + Std.Warn(). + Err(err). + Msgf("TryProcessOutbound: broadcast error for chain %d nonce %d", chainID, nonce) + return + } + + // report the outbound to the outbound tracker + signer.reportToOutboundTracker(ctx, zetacoreClient, chainID, nonce, txSig, logger) + return + } + if coinType != coin.CoinType_Gas { logger.Error(). Msgf("TryProcessOutbound: can only send SOL to the Solana network for chain %d nonce %d", chainID, nonce) diff --git a/zetaclient/chains/solana/signer/whitelist.go b/zetaclient/chains/solana/signer/whitelist.go new file mode 100644 index 0000000000..09a4ad8300 --- /dev/null +++ b/zetaclient/chains/solana/signer/whitelist.go @@ -0,0 +1,81 @@ +package signer + +import ( + "context" + + "cosmossdk.io/errors" + "github.com/gagliardetto/solana-go" + "github.com/gagliardetto/solana-go/rpc" + "github.com/near/borsh-go" + contracts "github.com/zeta-chain/node/pkg/contracts/solana" +) + +// SignWhitelistTx wraps the whitelist 'msg' into a Solana transaction and signs it with the relayer key. +func (signer *Signer) SignWhitelistTx(ctx context.Context, msg *contracts.MsgWhitelist) (*solana.Transaction, error) { + // create whitelist_spl_mint instruction with program call data + var err error + var inst solana.GenericInstruction + inst.DataBytes, err = borsh.Serialize(contracts.WhitelistInstructionParams{ + Discriminator: contracts.DiscriminatorWhitelistSplMint(), + }) + if err != nil { + return nil, errors.Wrap(err, "cannot serialize whitelist_spl_mint instruction") + } + + // attach required accounts to the instruction + privkey := signer.relayerKey + attachWhitelistAccounts(&inst, privkey.PublicKey(), signer.pda, msg.WhitelistCandidate(), signer.gatewayID) + + // get a recent blockhash + recent, err := signer.client.GetLatestBlockhash(ctx, rpc.CommitmentFinalized) + if err != nil { + return nil, errors.Wrap(err, "GetLatestBlockhash error") + } + + // create a transaction that wraps the instruction + tx, err := solana.NewTransaction( + []solana.Instruction{ + // TODO: outbound now uses 5K lamports as the fixed fee, we could explore priority fee and compute budget + // https://github.com/zeta-chain/node/issues/2599 + // programs.ComputeBudgetSetComputeUnitLimit(computeUnitLimit), + // programs.ComputeBudgetSetComputeUnitPrice(computeUnitPrice), + &inst}, + recent.Value.Blockhash, + solana.TransactionPayer(privkey.PublicKey()), + ) + if err != nil { + return nil, errors.Wrap(err, "NewTransaction error") + } + + // relayer signs the transaction + _, err = tx.Sign(func(key solana.PublicKey) *solana.PrivateKey { + if key.Equals(privkey.PublicKey()) { + return privkey + } + return nil + }) + if err != nil { + return nil, errors.Wrap(err, "signer unable to sign transaction") + } + + return tx, nil +} + +// attachWhitelistAccounts attaches the required accounts for the gateway whitelist instruction. +func attachWhitelistAccounts( + inst *solana.GenericInstruction, + signer solana.PublicKey, + pda solana.PublicKey, + whitelistCandidate solana.PublicKey, + gatewayID solana.PublicKey, +) { + // attach required accounts to the instruction + var accountSlice []*solana.AccountMeta + accountSlice = append(accountSlice, solana.Meta(whitelistCandidate)) + accountSlice = append(accountSlice, solana.Meta(pda).WRITE()) + accountSlice = append(accountSlice, solana.Meta(signer).WRITE().SIGNER()) + accountSlice = append(accountSlice, solana.Meta(solana.SystemProgramID)) + inst.ProgID = gatewayID + + inst.AccountValues = accountSlice +} diff --git a/zetaclient/testutils/constant.go b/zetaclient/testutils/constant.go index e9b8b53563..effa665443 100644 --- a/zetaclient/testutils/constant.go +++ b/zetaclient/testutils/constant.go @@ -42,7 +42,7 @@ const ( // GatewayAddresses contains constants gateway addresses for testing var GatewayAddresses = map[int64]string{ // Gateway address on Solana devnet - chains.SolanaDevnet.ChainId: "ZETAjseVjuFsxdRxo6MmTCvqFwb3ZHUx56Co3vCmGis", + chains.SolanaDevnet.ChainId: "BaDmykPHVwPQNY9SXQnJU8JPXdN89z3ib7qEfhNfkWRg", } // ConnectorAddresses contains constants ERC20 connector addresses for testing From 059d4d5a3c17ff57d3d4fc0a2fa55512c384cd80 Mon Sep 17 00:00:00 2001 From: skosito Date: Wed, 9 Oct 2024 20:09:24 +0200 Subject: [PATCH 02/20] whitelist test wip --- cmd/zetae2e/config/local.yml | 4 +-- cmd/zetae2e/config/localnet.yml | 4 +-- e2e/e2etests/test_solana_whitelist_spl.go | 26 ++++++++++++++----- e2e/runner/setup_solana.go | 1 - pkg/contracts/solana/gateway_message.go | 9 +++++-- zetaclient/chains/solana/observer/outbound.go | 15 +++++++++-- zetaclient/chains/solana/signer/signer.go | 22 ++++++++++++---- zetaclient/chains/solana/signer/whitelist.go | 4 ++- 8 files changed, 64 insertions(+), 21 deletions(-) diff --git a/cmd/zetae2e/config/local.yml b/cmd/zetae2e/config/local.yml index 2481ab31fa..f4a4456acd 100644 --- a/cmd/zetae2e/config/local.yml +++ b/cmd/zetae2e/config/local.yml @@ -76,8 +76,8 @@ policy_accounts: private_key: "0595CB0CD9BF5264A85A603EC8E43C30ADBB5FD2D9E2EF84C374EA4A65BB616C" observer_relayer_accounts: relayer_accounts: - - solana_address: "2qBVcNBZCubcnSR3NyCnFjCfkCVUB3G7ECPoaW5rxVjx" - solana_private_key: "3EMjCcCJg53fMEGVj13UPQpo6py9AKKyLE2qroR4yL1SvAN2tUznBvDKRYjntw7m6Jof1R2CSqjTddL27rEb6sFQ" + - solana_address: "37yGiHAnLvWZUNVwu9esp74YQFqxU1qHCbABkDvRddUQ" + solana_private_key: "4yqSQxDeTBvn86BuxcN5jmZb2gaobFXrBqu8kiE9rZxNkVMe3LfXmFigRsU4sRp7vk4vVP1ZCFiejDKiXBNWvs2C" - solana_address: "4kkCV8H38xirwQTkE5kL6FHNtYGHnMQQ7SkCjAxibHFK" solana_private_key: "5SSv7jWzamtjWNKGiKf3gvCPHcq9mE5x6LhYgzJCKNSxoQ83gFpmMgmg2JS2zdKcBEdwy7y9bvWgX4LBiUpvnrPf" rpcs: diff --git a/cmd/zetae2e/config/localnet.yml b/cmd/zetae2e/config/localnet.yml index 5bd207a020..4a17827c94 100644 --- a/cmd/zetae2e/config/localnet.yml +++ b/cmd/zetae2e/config/localnet.yml @@ -76,8 +76,8 @@ policy_accounts: private_key: "0595CB0CD9BF5264A85A603EC8E43C30ADBB5FD2D9E2EF84C374EA4A65BB616C" observer_relayer_accounts: relayer_accounts: - - solana_address: "2qBVcNBZCubcnSR3NyCnFjCfkCVUB3G7ECPoaW5rxVjx" - solana_private_key: "3EMjCcCJg53fMEGVj13UPQpo6py9AKKyLE2qroR4yL1SvAN2tUznBvDKRYjntw7m6Jof1R2CSqjTddL27rEb6sFQ" + - solana_address: "37yGiHAnLvWZUNVwu9esp74YQFqxU1qHCbABkDvRddUQ" + solana_private_key: "4yqSQxDeTBvn86BuxcN5jmZb2gaobFXrBqu8kiE9rZxNkVMe3LfXmFigRsU4sRp7vk4vVP1ZCFiejDKiXBNWvs2C" - solana_address: "4kkCV8H38xirwQTkE5kL6FHNtYGHnMQQ7SkCjAxibHFK" solana_private_key: "5SSv7jWzamtjWNKGiKf3gvCPHcq9mE5x6LhYgzJCKNSxoQ83gFpmMgmg2JS2zdKcBEdwy7y9bvWgX4LBiUpvnrPf" rpcs: diff --git a/e2e/e2etests/test_solana_whitelist_spl.go b/e2e/e2etests/test_solana_whitelist_spl.go index 5276d78552..6af27f614c 100644 --- a/e2e/e2etests/test_solana_whitelist_spl.go +++ b/e2e/e2etests/test_solana_whitelist_spl.go @@ -20,14 +20,23 @@ func TestSolanaWhitelistSPL(r *runner.E2ERunner, args []string) { spl := r.DeploySPL(&privkey) - // whitelist erc20 zrc20 - r.Logger.Info("whitelisting ERC20 on new network") + // check that whitelist entry doesn't exist for this spl + seed := [][]byte{[]byte("whitelist"), spl.PublicKey().Bytes()} + whitelistEntryPDA, _, err := solana.FindProgramAddress(seed, r.GatewayProgram) + require.NoError(r, err) + + whitelistEntryInfo, err := r.SolanaClient.GetAccountInfo(r.Ctx, whitelistEntryPDA) + require.Error(r, err) + require.Nil(r, whitelistEntryInfo) + + // whitelist sol zrc20 + r.Logger.Info("whitelisting spl on new network") res, err := r.ZetaTxServer.BroadcastTx(utils.AdminPolicyName, crosschaintypes.NewMsgWhitelistERC20( r.ZetaTxServer.MustGetAccountAddressFromName(utils.AdminPolicyName), spl.PublicKey().String(), chains.SolanaLocalnet.ChainId, - "NEWSOLERC20", - "NEWSOLERC20", + "TESTSPL", + "TESTSPL", 6, 100000, )) @@ -37,10 +46,10 @@ func TestSolanaWhitelistSPL(r *runner.E2ERunner, args []string) { whitelistCCTXIndex, err := txserver.FetchAttributeFromTxResponse(res, "whitelist_cctx_index") require.NoError(r, err) - erc20zrc20Addr, err := txserver.FetchAttributeFromTxResponse(res, "zrc20_address") + zrc20Addr, err := txserver.FetchAttributeFromTxResponse(res, "zrc20_address") require.NoError(r, err) - err = r.ZetaTxServer.InitializeLiquidityCap(erc20zrc20Addr) + err = r.ZetaTxServer.InitializeLiquidityCap(zrc20Addr) require.NoError(r, err) // ensure CCTX created @@ -52,4 +61,9 @@ func TestSolanaWhitelistSPL(r *runner.E2ERunner, args []string) { // wait for the whitelist cctx to be mined r.WaitForMinedCCTXFromIndex(whitelistCCTXIndex) + + // check that whitelist entry exists for this spl + whitelistEntryInfo, err = r.SolanaClient.GetAccountInfo(r.Ctx, whitelistEntryPDA) + require.NoError(r, err) + require.NotNil(r, whitelistEntryInfo) } diff --git a/e2e/runner/setup_solana.go b/e2e/runner/setup_solana.go index d5c4bb20de..a4ec0a79d7 100644 --- a/e2e/runner/setup_solana.go +++ b/e2e/runner/setup_solana.go @@ -43,7 +43,6 @@ func (r *E2ERunner) SetSolanaContracts(deployerPrivateKey string) { accountSlice = append(accountSlice, solana.Meta(privkey.PublicKey()).WRITE().SIGNER()) accountSlice = append(accountSlice, solana.Meta(pdaComputed).WRITE()) accountSlice = append(accountSlice, solana.Meta(solana.SystemProgramID)) - accountSlice = append(accountSlice, solana.Meta(r.GatewayProgram)) inst.ProgID = r.GatewayProgram inst.AccountValues = accountSlice diff --git a/pkg/contracts/solana/gateway_message.go b/pkg/contracts/solana/gateway_message.go index 1f31e1d63b..f75665a5f5 100644 --- a/pkg/contracts/solana/gateway_message.go +++ b/pkg/contracts/solana/gateway_message.go @@ -110,14 +110,19 @@ func (msg *MsgWithdraw) Signer() (common.Address, error) { type MsgWhitelist struct { // whitelistCandidate is the whitelist candidate whitelistCandidate solana.PublicKey + whitelistEntry solana.PublicKey } // NewMsgWhitelist returns a new whitelist_spl_mint message -func NewMsgWhitelist(whitelistCandidate solana.PublicKey) *MsgWhitelist { - return &MsgWhitelist{whitelistCandidate: whitelistCandidate} +func NewMsgWhitelist(whitelistCandidate solana.PublicKey, whitelistEntry solana.PublicKey) *MsgWhitelist { + return &MsgWhitelist{whitelistCandidate: whitelistCandidate, whitelistEntry: whitelistEntry} } // To returns the recipient address of the message func (msg *MsgWhitelist) WhitelistCandidate() solana.PublicKey { return msg.whitelistCandidate } + +func (msg *MsgWhitelist) WhitelistEntry() solana.PublicKey { + return msg.whitelistEntry +} diff --git a/zetaclient/chains/solana/observer/outbound.go b/zetaclient/chains/solana/observer/outbound.go index 7ff968ea93..88a1337f7f 100644 --- a/zetaclient/chains/solana/observer/outbound.go +++ b/zetaclient/chains/solana/observer/outbound.go @@ -155,8 +155,11 @@ func (ob *Observer) VoteOutboundIfConfirmed(ctx context.Context, cctx *crosschai return false, errors.Wrapf(err, "ParseGatewayInstruction error for sig %s", txSig) } - // the amount and status of the outbound - outboundAmount := new(big.Int).SetUint64(inst.TokenAmount()) + outboundAmount := new(big.Int).SetUint64(0) + if inst != nil { + // the amount and status of the outbound + outboundAmount = new(big.Int).SetUint64(inst.TokenAmount()) + } // status was already verified as successful in CheckFinalizedTx outboundStatus := chains.ReceiveStatus_success @@ -295,6 +298,10 @@ func (ob *Observer) CheckFinalizedTx( logger.Error().Err(err).Msg("ParseGatewayInstruction error") return nil, false } + if inst == nil { + return txResult, true + } + txNonce := inst.GatewayNonce() // recover ECDSA signer from instruction @@ -352,6 +359,10 @@ func ParseGatewayInstruction( switch coinType { case coin.CoinType_Gas: return contracts.ParseInstructionWithdraw(instruction) + // for these currently parsing is not needed since instructions are empty, and these instructions dont implement OutboundInstruction interface + // can be revisited as more are added + case coin.CoinType_Cmd: + return nil, nil default: return nil, fmt.Errorf("unsupported outbound coin type %s", coinType) } diff --git a/zetaclient/chains/solana/signer/signer.go b/zetaclient/chains/solana/signer/signer.go index a77f456c32..c3acc5e4e5 100644 --- a/zetaclient/chains/solana/signer/signer.go +++ b/zetaclient/chains/solana/signer/signer.go @@ -2,7 +2,6 @@ package signer import ( "context" - "fmt" "strings" "cosmossdk.io/errors" @@ -127,18 +126,31 @@ func (signer *Signer) TryProcessOutbound( if len(relayedMsg) != 2 { return } - fmt.Println("debug ", coinType, relayedMsg[1]) if coinType == coin.CoinType_Cmd { pk, err := solana.PublicKeyFromBase58(relayedMsg[1]) if err != nil { - fmt.Println("err decoding ", err.Error()) + signer.Logger(). + Std.Error(). + Err(err). + Msgf("TryProcessOutbound: error decoding spl from relayed msg") + return + } + + seed := [][]byte{[]byte("whitelist"), pk.Bytes()} + whitelistEntryPDA, _, err := solana.FindProgramAddress(seed, signer.gatewayID) + if err != nil { + signer.Logger(). + Std.Error(). + Err(err). + Msgf("TryProcessOutbound: error calculating whitelistEntry pda") + return } // sign the withdraw transaction by relayer key - tx, err := signer.SignWhitelistTx(ctx, contracts.NewMsgWhitelist(pk)) + tx, err := signer.SignWhitelistTx(ctx, contracts.NewMsgWhitelist(pk, whitelistEntryPDA)) if err != nil { - logger.Error().Err(err).Msgf("TryProcessOutbound: SignGasWithdraw error for chain %d nonce %d", chainID, nonce) + logger.Error().Err(err).Msgf("TryProcessOutbound: SignWhitelistTx error for chain %d nonce %d", chainID, nonce) return } diff --git a/zetaclient/chains/solana/signer/whitelist.go b/zetaclient/chains/solana/signer/whitelist.go index 09a4ad8300..c3798ca92f 100644 --- a/zetaclient/chains/solana/signer/whitelist.go +++ b/zetaclient/chains/solana/signer/whitelist.go @@ -24,7 +24,7 @@ func (signer *Signer) SignWhitelistTx(ctx context.Context, msg *contracts.MsgWhi // attach required accounts to the instruction privkey := signer.relayerKey - attachWhitelistAccounts(&inst, privkey.PublicKey(), signer.pda, msg.WhitelistCandidate(), signer.gatewayID) + attachWhitelistAccounts(&inst, privkey.PublicKey(), signer.pda, msg.WhitelistCandidate(), msg.WhitelistEntry(), signer.gatewayID) // get a recent blockhash recent, err := signer.client.GetLatestBlockhash(ctx, rpc.CommitmentFinalized) @@ -67,10 +67,12 @@ func attachWhitelistAccounts( signer solana.PublicKey, pda solana.PublicKey, whitelistCandidate solana.PublicKey, + whitelistEntry solana.PublicKey, gatewayID solana.PublicKey, ) { // attach required accounts to the instruction var accountSlice []*solana.AccountMeta + accountSlice = append(accountSlice, solana.Meta(whitelistEntry).WRITE()) accountSlice = append(accountSlice, solana.Meta(whitelistCandidate)) accountSlice = append(accountSlice, solana.Meta(pda).WRITE()) accountSlice = append(accountSlice, solana.Meta(signer).WRITE().SIGNER()) From 4897a2ccfe832ad9eb8a41bcec0ec21f1985c343 Mon Sep 17 00:00:00 2001 From: skosito Date: Wed, 9 Oct 2024 23:38:28 +0200 Subject: [PATCH 03/20] test fixes --- cmd/zetae2e/local/local.go | 1 + pkg/contracts/solana/gateway_message.go | 2 ++ zetaclient/chains/solana/signer/signer.go | 9 +++++---- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/cmd/zetae2e/local/local.go b/cmd/zetae2e/local/local.go index 4a7fbef39c..eda38c3380 100644 --- a/cmd/zetae2e/local/local.go +++ b/cmd/zetae2e/local/local.go @@ -395,6 +395,7 @@ func localE2ETest(cmd *cobra.Command, _ []string) { e2etests.TestSolanaDepositAndCallRefundName, e2etests.TestSolanaDepositRestrictedName, e2etests.TestSolanaWithdrawRestrictedName, + e2etests.TestSolanaWhitelistSPLName, } eg.Go(solanaTestRoutine(conf, deployerRunner, verbose, solanaTests...)) } diff --git a/pkg/contracts/solana/gateway_message.go b/pkg/contracts/solana/gateway_message.go index f75665a5f5..5c8b4ac49b 100644 --- a/pkg/contracts/solana/gateway_message.go +++ b/pkg/contracts/solana/gateway_message.go @@ -61,6 +61,8 @@ func (msg *MsgWithdraw) Hash() [32]byte { var message []byte buff := make([]byte, 8) + message = append(message, []byte("withdraw")...) + binary.BigEndian.PutUint64(buff, msg.chainID) message = append(message, buff...) diff --git a/zetaclient/chains/solana/signer/signer.go b/zetaclient/chains/solana/signer/signer.go index c3acc5e4e5..4711ef0cff 100644 --- a/zetaclient/chains/solana/signer/signer.go +++ b/zetaclient/chains/solana/signer/signer.go @@ -122,12 +122,13 @@ func (signer *Signer) TryProcessOutbound( chainID := signer.Chain().ChainId nonce := params.TssNonce coinType := cctx.InboundParams.CoinType - relayedMsg := strings.Split(cctx.RelayedMessage, ":") - if len(relayedMsg) != 2 { - return - } if coinType == coin.CoinType_Cmd { + relayedMsg := strings.Split(cctx.RelayedMessage, ":") + if len(relayedMsg) != 2 { + return + } + pk, err := solana.PublicKeyFromBase58(relayedMsg[1]) if err != nil { signer.Logger(). From b95097ae93e9472040d1ea64422a9d9fcb8b8986 Mon Sep 17 00:00:00 2001 From: skosito Date: Wed, 9 Oct 2024 23:40:36 +0200 Subject: [PATCH 04/20] fmt --- e2e/e2etests/test_solana_whitelist_spl.go | 1 + e2e/runner/solana.go | 6 +++++- zetaclient/chains/solana/signer/signer.go | 4 +++- zetaclient/chains/solana/signer/whitelist.go | 10 +++++++++- 4 files changed, 18 insertions(+), 3 deletions(-) diff --git a/e2e/e2etests/test_solana_whitelist_spl.go b/e2e/e2etests/test_solana_whitelist_spl.go index 6af27f614c..6c71c0d214 100644 --- a/e2e/e2etests/test_solana_whitelist_spl.go +++ b/e2e/e2etests/test_solana_whitelist_spl.go @@ -3,6 +3,7 @@ package e2etests import ( "github.com/gagliardetto/solana-go" "github.com/stretchr/testify/require" + "github.com/zeta-chain/node/e2e/runner" "github.com/zeta-chain/node/e2e/txserver" "github.com/zeta-chain/node/e2e/utils" diff --git a/e2e/runner/solana.go b/e2e/runner/solana.go index e4ed5162df..cf9875dd9c 100644 --- a/e2e/runner/solana.go +++ b/e2e/runner/solana.go @@ -117,7 +117,11 @@ func (r *E2ERunner) DeploySPL(privateKey *solana.PrivateKey) *solana.Wallet { tokenAccount.PublicKey(), ).Build() - signedTx := r.CreateSignedTransaction([]solana.Instruction{createAccountInstruction, initializeMintInstruction}, *privateKey, []solana.PrivateKey{tokenAccount.PrivateKey}) + signedTx := r.CreateSignedTransaction( + []solana.Instruction{createAccountInstruction, initializeMintInstruction}, + *privateKey, + []solana.PrivateKey{tokenAccount.PrivateKey}, + ) // broadcast the transaction and wait for finalization _, out := r.BroadcastTxSync(signedTx) diff --git a/zetaclient/chains/solana/signer/signer.go b/zetaclient/chains/solana/signer/signer.go index 4711ef0cff..95e4819774 100644 --- a/zetaclient/chains/solana/signer/signer.go +++ b/zetaclient/chains/solana/signer/signer.go @@ -151,7 +151,9 @@ func (signer *Signer) TryProcessOutbound( // sign the withdraw transaction by relayer key tx, err := signer.SignWhitelistTx(ctx, contracts.NewMsgWhitelist(pk, whitelistEntryPDA)) if err != nil { - logger.Error().Err(err).Msgf("TryProcessOutbound: SignWhitelistTx error for chain %d nonce %d", chainID, nonce) + logger.Error(). + Err(err). + Msgf("TryProcessOutbound: SignWhitelistTx error for chain %d nonce %d", chainID, nonce) return } diff --git a/zetaclient/chains/solana/signer/whitelist.go b/zetaclient/chains/solana/signer/whitelist.go index c3798ca92f..858308aeb3 100644 --- a/zetaclient/chains/solana/signer/whitelist.go +++ b/zetaclient/chains/solana/signer/whitelist.go @@ -7,6 +7,7 @@ import ( "github.com/gagliardetto/solana-go" "github.com/gagliardetto/solana-go/rpc" "github.com/near/borsh-go" + contracts "github.com/zeta-chain/node/pkg/contracts/solana" ) @@ -24,7 +25,14 @@ func (signer *Signer) SignWhitelistTx(ctx context.Context, msg *contracts.MsgWhi // attach required accounts to the instruction privkey := signer.relayerKey - attachWhitelistAccounts(&inst, privkey.PublicKey(), signer.pda, msg.WhitelistCandidate(), msg.WhitelistEntry(), signer.gatewayID) + attachWhitelistAccounts( + &inst, + privkey.PublicKey(), + signer.pda, + msg.WhitelistCandidate(), + msg.WhitelistEntry(), + signer.gatewayID, + ) // get a recent blockhash recent, err := signer.client.GetLatestBlockhash(ctx, rpc.CommitmentFinalized) From 41ae9b18744fea485fb09db746af59a73a1a0049 Mon Sep 17 00:00:00 2001 From: skosito Date: Mon, 14 Oct 2024 10:11:21 +0200 Subject: [PATCH 05/20] small refactoring --- zetaclient/chains/solana/signer/signer.go | 98 +++++++++-------------- 1 file changed, 39 insertions(+), 59 deletions(-) diff --git a/zetaclient/chains/solana/signer/signer.go b/zetaclient/chains/solana/signer/signer.go index 95e4819774..9c68443a6f 100644 --- a/zetaclient/chains/solana/signer/signer.go +++ b/zetaclient/chains/solana/signer/signer.go @@ -123,7 +123,10 @@ func (signer *Signer) TryProcessOutbound( nonce := params.TssNonce coinType := cctx.InboundParams.CoinType - if coinType == coin.CoinType_Cmd { + var tx *solana.Transaction + + switch coinType { + case coin.CoinType_Cmd: relayedMsg := strings.Split(cctx.RelayedMessage, ":") if len(relayedMsg) != 2 { return @@ -149,7 +152,7 @@ func (signer *Signer) TryProcessOutbound( } // sign the withdraw transaction by relayer key - tx, err := signer.SignWhitelistTx(ctx, contracts.NewMsgWhitelist(pk, whitelistEntryPDA)) + tx, err = signer.SignWhitelistTx(ctx, contracts.NewMsgWhitelist(pk, whitelistEntryPDA)) if err != nil { logger.Error(). Err(err). @@ -157,70 +160,47 @@ func (signer *Signer) TryProcessOutbound( return } - // broadcast the signed tx to the Solana network with preflight check - txSig, err := signer.client.SendTransactionWithOpts( - ctx, - tx, - // Commitment "finalized" is too conservative for preflight check and - // it results in repeated broadcast attempts that only 1 will succeed. - // Commitment "processed" will simulate tx against more recent state - // thus fails faster once a tx is already broadcasted and processed by the cluster. - // This reduces the number of "failed" txs due to repeated broadcast attempts. - rpc.TransactionOpts{PreflightCommitment: rpc.CommitmentProcessed}, - ) + case coin.CoinType_Gas: + // compliance check + cancelTx := compliance.IsCctxRestricted(cctx) + if cancelTx { + compliance.PrintComplianceLog( + logger, + signer.Logger().Compliance, + true, + chainID, + cctx.Index, + cctx.InboundParams.Sender, + params.Receiver, + "SOL", + ) + } + + // sign gateway withdraw message by TSS + msg, err := signer.SignMsgWithdraw(ctx, params, height, cancelTx) if err != nil { - signer.Logger(). - Std.Warn(). - Err(err). - Msgf("TryProcessOutbound: broadcast error for chain %d nonce %d", chainID, nonce) + logger.Error().Err(err).Msgf("TryProcessOutbound: SignMsgWithdraw error for chain %d nonce %d", chainID, nonce) return } - // report the outbound to the outbound tracker - signer.reportToOutboundTracker(ctx, zetacoreClient, chainID, nonce, txSig, logger) - return - } - - if coinType != coin.CoinType_Gas { - logger.Error(). - Msgf("TryProcessOutbound: can only send SOL to the Solana network for chain %d nonce %d", chainID, nonce) - return - } - - // compliance check - cancelTx := compliance.IsCctxRestricted(cctx) - if cancelTx { - compliance.PrintComplianceLog( - logger, - signer.Logger().Compliance, - true, - chainID, - cctx.Index, - cctx.InboundParams.Sender, - params.Receiver, - "SOL", - ) - } - - // sign gateway withdraw message by TSS - msg, err := signer.SignMsgWithdraw(ctx, params, height, cancelTx) - if err != nil { - logger.Error().Err(err).Msgf("TryProcessOutbound: SignMsgWithdraw error for chain %d nonce %d", chainID, nonce) - return - } + // skip relaying the transaction if this signer hasn't set the relayer key + if !signer.HasRelayerKey() { + return + } - // skip relaying the transaction if this signer hasn't set the relayer key - if !signer.HasRelayerKey() { - return - } + // set relayer balance metrics + signer.SetRelayerBalanceMetrics(ctx) - // set relayer balance metrics - signer.SetRelayerBalanceMetrics(ctx) + // sign the withdraw transaction by relayer key + tx, err = signer.SignWithdrawTx(ctx, *msg) + if err != nil { + logger.Error().Err(err).Msgf("TryProcessOutbound: SignGasWithdraw error for chain %d nonce %d", chainID, nonce) + return + } - // sign the withdraw transaction by relayer key - tx, err := signer.SignWithdrawTx(ctx, *msg) - if err != nil { - logger.Error().Err(err).Msgf("TryProcessOutbound: SignGasWithdraw error for chain %d nonce %d", chainID, nonce) + default: + logger.Error(). + Msgf("TryProcessOutbound: can only send SOL to the Solana network for chain %d nonce %d", chainID, nonce) return } From 3d071cacb4a28c6843e0c46a27a28cb400fa06b5 Mon Sep 17 00:00:00 2001 From: skosito Date: Fri, 25 Oct 2024 01:21:00 +0200 Subject: [PATCH 06/20] add protocol contracts solana pkg --- go.mod | 5 ++--- go.sum | 4 ++++ pkg/contracts/solana/gateway.go | 13 +++++++------ 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 524a63d8be..b80f4af402 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,6 @@ module github.com/zeta-chain/node -go 1.22.2 - -toolchain go1.22.5 +go 1.22.8 require ( cosmossdk.io/errors v1.0.1 @@ -336,6 +334,7 @@ require ( github.com/bnb-chain/tss-lib v1.5.0 github.com/showa-93/go-mask v0.6.2 github.com/tonkeeper/tongo v1.9.3 + github.com/zeta-chain/protocol-contracts-solana/go-idl v0.0.0-20241024230448-593397669504 ) require ( diff --git a/go.sum b/go.sum index 04150048bc..39449e8024 100644 --- a/go.sum +++ b/go.sum @@ -2186,6 +2186,8 @@ github.com/fzipp/gocyclo v0.5.1/go.mod h1:rXPyn8fnlpa0R2csP/31uerbiVBugk5whMdlya github.com/fzipp/gocyclo v0.6.0/go.mod h1:rXPyn8fnlpa0R2csP/31uerbiVBugk5whMdlyaLkLoA= github.com/gagliardetto/binary v0.8.0 h1:U9ahc45v9HW0d15LoN++vIXSJyqR/pWw8DDlhd7zvxg= github.com/gagliardetto/binary v0.8.0/go.mod h1:2tfj51g5o9dnvsc+fL3Jxr22MuWzYXwx9wEoN0XQ7/c= +github.com/gagliardetto/gofuzz v1.2.2 h1:XL/8qDMzcgvR4+CyRQW9UGdwPRPMHVJfqQ/uMvSUuQw= +github.com/gagliardetto/gofuzz v1.2.2/go.mod h1:bkH/3hYLZrMLbfYWA0pWzXmi5TTRZnu4pMGZBkqMKvY= github.com/gagliardetto/solana-go v1.10.0 h1:lDuHGC+XLxw9j8fCHBZM9tv4trI0PVhev1m9NAMaIdM= github.com/gagliardetto/solana-go v1.10.0/go.mod h1:afBEcIRrDLJst3lvAahTr63m6W2Ns6dajZxe2irF7Jg= github.com/gagliardetto/treeout v0.1.4 h1:ozeYerrLCmCubo1TcIjFiOWTTGteOOHND1twdFpgwaw= @@ -4210,6 +4212,8 @@ github.com/zeta-chain/keystone/keys v0.0.0-20240826165841-3874f358c138 h1:vck/Fc github.com/zeta-chain/keystone/keys v0.0.0-20240826165841-3874f358c138/go.mod h1:U494OsZTWsU75hqoriZgMdSsgSGP1mUL1jX+wN/Aez8= github.com/zeta-chain/protocol-contracts v1.0.2-athens3.0.20241021075719-d40d2e28467c h1:ZoFxMMZtivRLquXVq1sEVlT45UnTPMO1MSXtc88nDv4= github.com/zeta-chain/protocol-contracts v1.0.2-athens3.0.20241021075719-d40d2e28467c/go.mod h1:SjT7QirtJE8stnAe1SlNOanxtfSfijJm3MGJ+Ax7w7w= +github.com/zeta-chain/protocol-contracts-solana/go-idl v0.0.0-20241024230448-593397669504 h1:0h1yiSiCnh5g9Zp/ReDyeQZh7hp1UZ76A6BFDvnAX8Q= +github.com/zeta-chain/protocol-contracts-solana/go-idl v0.0.0-20241024230448-593397669504/go.mod h1:AaM8C6ygo7Ms7BjRx5243cyRyMZItFsu7PWXedX8u4g= github.com/zeta-chain/tss-lib v0.0.0-20240916163010-2e6b438bd901 h1:9whtN5fjYHfk4yXIuAsYP2EHxImwDWDVUOnZJ2pfL3w= github.com/zeta-chain/tss-lib v0.0.0-20240916163010-2e6b438bd901/go.mod h1:d2iTC62s9JwKiCMPhcDDXbIZmuzAyJ4lwso0H5QyRbk= github.com/zondax/hid v0.9.1/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM= diff --git a/pkg/contracts/solana/gateway.go b/pkg/contracts/solana/gateway.go index 5049a3c668..3c1f1a2425 100644 --- a/pkg/contracts/solana/gateway.go +++ b/pkg/contracts/solana/gateway.go @@ -4,6 +4,7 @@ package solana import ( "github.com/gagliardetto/solana-go" "github.com/pkg/errors" + idlgateway "github.com/zeta-chain/protocol-contracts-solana/go-idl/generated" ) const ( @@ -20,32 +21,32 @@ const ( // DiscriminatorInitialize returns the discriminator for Solana gateway 'initialize' instruction func DiscriminatorInitialize() [8]byte { - return [8]byte{175, 175, 109, 31, 13, 152, 155, 237} + return idlgateway.IDLGateway.GetDiscriminator("initialize") } // DiscriminatorDeposit returns the discriminator for Solana gateway 'deposit' instruction func DiscriminatorDeposit() [8]byte { - return [8]byte{242, 35, 198, 137, 82, 225, 242, 182} + return idlgateway.IDLGateway.GetDiscriminator("deposit") } // DiscriminatorDepositSPL returns the discriminator for Solana gateway 'deposit_spl_token' instruction func DiscriminatorDepositSPL() [8]byte { - return [8]byte{86, 172, 212, 121, 63, 233, 96, 144} + return idlgateway.IDLGateway.GetDiscriminator("deposit_spl_token") } // DiscriminatorWithdraw returns the discriminator for Solana gateway 'withdraw' instruction func DiscriminatorWithdraw() [8]byte { - return [8]byte{183, 18, 70, 156, 148, 109, 161, 34} + return idlgateway.IDLGateway.GetDiscriminator("withdraw") } // DiscriminatorWithdrawSPL returns the discriminator for Solana gateway 'withdraw_spl_token' instruction func DiscriminatorWithdrawSPL() [8]byte { - return [8]byte{156, 234, 11, 89, 235, 246, 32} + return idlgateway.IDLGateway.GetDiscriminator("withdraw_spl_token") } // DiscriminatorWhitelist returns the discriminator for Solana gateway 'whitelist_spl_mint' instruction func DiscriminatorWhitelistSplMint() [8]byte { - return [8]byte{30, 110, 162, 42, 208, 147, 254, 219} + return idlgateway.IDLGateway.GetDiscriminator("whitelist_spl_mint") } // ParseGatewayAddressAndPda parses the gateway id and program derived address from the given string From f5b5ebd9e29dcccf6416a03cadcfb9215ca0c616 Mon Sep 17 00:00:00 2001 From: skosito Date: Fri, 25 Oct 2024 01:44:50 +0200 Subject: [PATCH 07/20] bump go idl pkg --- go.mod | 6 ++++-- go.sum | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index b80f4af402..fde7bd9929 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,8 @@ module github.com/zeta-chain/node -go 1.22.8 +go 1.22.2 + +toolchain go1.22.5 require ( cosmossdk.io/errors v1.0.1 @@ -334,7 +336,7 @@ require ( github.com/bnb-chain/tss-lib v1.5.0 github.com/showa-93/go-mask v0.6.2 github.com/tonkeeper/tongo v1.9.3 - github.com/zeta-chain/protocol-contracts-solana/go-idl v0.0.0-20241024230448-593397669504 + github.com/zeta-chain/protocol-contracts-solana/go-idl v0.0.0-20241024234335-31d1270d9b34 ) require ( diff --git a/go.sum b/go.sum index 39449e8024..c0d6e18072 100644 --- a/go.sum +++ b/go.sum @@ -4212,8 +4212,8 @@ github.com/zeta-chain/keystone/keys v0.0.0-20240826165841-3874f358c138 h1:vck/Fc github.com/zeta-chain/keystone/keys v0.0.0-20240826165841-3874f358c138/go.mod h1:U494OsZTWsU75hqoriZgMdSsgSGP1mUL1jX+wN/Aez8= github.com/zeta-chain/protocol-contracts v1.0.2-athens3.0.20241021075719-d40d2e28467c h1:ZoFxMMZtivRLquXVq1sEVlT45UnTPMO1MSXtc88nDv4= github.com/zeta-chain/protocol-contracts v1.0.2-athens3.0.20241021075719-d40d2e28467c/go.mod h1:SjT7QirtJE8stnAe1SlNOanxtfSfijJm3MGJ+Ax7w7w= -github.com/zeta-chain/protocol-contracts-solana/go-idl v0.0.0-20241024230448-593397669504 h1:0h1yiSiCnh5g9Zp/ReDyeQZh7hp1UZ76A6BFDvnAX8Q= -github.com/zeta-chain/protocol-contracts-solana/go-idl v0.0.0-20241024230448-593397669504/go.mod h1:AaM8C6ygo7Ms7BjRx5243cyRyMZItFsu7PWXedX8u4g= +github.com/zeta-chain/protocol-contracts-solana/go-idl v0.0.0-20241024234335-31d1270d9b34 h1:sotIvl/BFx6Yz0gfGIbIRuyCuvg07X3627k86Nw+v6c= +github.com/zeta-chain/protocol-contracts-solana/go-idl v0.0.0-20241024234335-31d1270d9b34/go.mod h1:DcDY828o773soiU/h0XpC+naxitrIMFVZqEvq/EJxMA= github.com/zeta-chain/tss-lib v0.0.0-20240916163010-2e6b438bd901 h1:9whtN5fjYHfk4yXIuAsYP2EHxImwDWDVUOnZJ2pfL3w= github.com/zeta-chain/tss-lib v0.0.0-20240916163010-2e6b438bd901/go.mod h1:d2iTC62s9JwKiCMPhcDDXbIZmuzAyJ4lwso0H5QyRbk= github.com/zondax/hid v0.9.1/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM= From f5204ca61993463357c7a9c00aa0a87ebcd6443c Mon Sep 17 00:00:00 2001 From: skosito Date: Mon, 28 Oct 2024 11:12:21 +0100 Subject: [PATCH 08/20] revert back to development gateway keypair --- contrib/localnet/solana/gateway-keypair.json | 2 +- contrib/localnet/solana/gateway.so | Bin 326472 -> 356384 bytes pkg/contracts/solana/gateway.go | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/contrib/localnet/solana/gateway-keypair.json b/contrib/localnet/solana/gateway-keypair.json index daab863409..99c8b61dee 100644 --- a/contrib/localnet/solana/gateway-keypair.json +++ b/contrib/localnet/solana/gateway-keypair.json @@ -1 +1 @@ -[170,171,179,164,93,78,137,61,23,64,233,109,110,253,36,57,111,127,55,106,199,85,39,175,73,206,30,244,229,216,101,145,157,23,123,51,143,17,39,82,72,82,114,55,41,2,101,170,230,6,41,179,227,225,43,107,236,186,181,51,118,110,43,227] \ No newline at end of file +[148,138,110,3,169,253,42,101,79,110,149,110,112,214,41,163,75,28,36,29,241,151,41,200,135,185,252,180,158,191,166,156,119,192,217,18,69,149,119,145,212,43,144,149,176,111,89,140,102,63,193,127,241,148,51,161,170,62,19,196,239,253,6,192] \ No newline at end of file diff --git a/contrib/localnet/solana/gateway.so b/contrib/localnet/solana/gateway.so index 3f991179619d09e68a922f3612fc85d0aa0ae666..186bac2cf6beac74188987173bb5eecfd508484b 100755 GIT binary patch delta 87215 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

29sOW2RjF`gn?JPNE5VPG<`=3vaNdrfZ^E5h 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 OJ@V?(&i0EY7XL3YsP|0( diff --git a/pkg/contracts/solana/gateway.go b/pkg/contracts/solana/gateway.go index 3c1f1a2425..bf5ef91e15 100644 --- a/pkg/contracts/solana/gateway.go +++ b/pkg/contracts/solana/gateway.go @@ -9,7 +9,7 @@ import ( const ( // SolanaGatewayProgramID is the program ID of the Solana gateway program - SolanaGatewayProgramID = "BaDmykPHVwPQNY9SXQnJU8JPXdN89z3ib7qEfhNfkWRg" + SolanaGatewayProgramID = "94U5AHQMKkV5txNJ17QPXWoh474PheGou6cNP2FEuL1d" // PDASeed is the seed for the Solana gateway program derived address PDASeed = "meta" From 43fbe697fca2c0663d3b8c8d7910a0c737cf34bb Mon Sep 17 00:00:00 2001 From: skosito Date: Mon, 28 Oct 2024 15:54:17 +0100 Subject: [PATCH 09/20] fix e2e test --- contrib/localnet/solana/gateway.so | Bin 356384 -> 356384 bytes go.mod | 2 +- go.sum | 4 +- pkg/contracts/solana/gateway.json | 109 +++++++++++++++++++++- zetaclient/chains/solana/signer/signer.go | 8 +- zetaclient/testutils/constant.go | 2 +- 6 files changed, 117 insertions(+), 8 deletions(-) diff --git a/contrib/localnet/solana/gateway.so b/contrib/localnet/solana/gateway.so index 186bac2cf6beac74188987173bb5eecfd508484b..6af69d7cf5e6ac6b3fd2a8aa849be21f8ef9eb74 100755 GIT binary patch delta 1529 zcmZ4RKy<+a(G7b9c*+ml6k-4Z*QwU$z6&1vFViChrk+;6XNJvVpt^ z53=^j0rE0D$oe)12bS z#BG?+U8yp;MiM#`TII1fzz{QaRWx9ePv*hGJDQi-l`9O*VBQSet6_6n$ zx^A+7xXI=<@*AWv1G2{&Js_!>Dr0O&PL&^Q&{L&@i^=2|2lP~lh(FZ$MmQfez7eSy zIld<=xav$caU?laZg6ZtOO*~T=+R9z7m^&^8yrnG|8Z4d$Bb?tU-amvX1-DMBRSt} z@Dm9@b>@Y6Dk#Z&(tNanOkqB1LDuXsf4j$g#xt xuh3)yV&?4?nk<6d3ZUW|RDOd}7^r9lr6^EQ3`#-ZqF3(%i}v;_TUdN{0|4Oa=gt5C delta 1529 zcmZ4RKy<+a(G7b9csLwSPhbFo;|I&kCV!Asnfyi|BLrEnTI$AskOB!t28Km)r`m#pPd}j{=GNY^ZjqmU;mf{}Nhd3KBmPoqb}ufF_H`NUWAsuFPXPQjjBN~C`OoVfgEkJe-vOU#OAFw)e@&!4a$y3BMNOh%v zxD6A!D^(`fNTLVk0l2NGt~>!3M|I@|xHxj4Ojj^umYf_YWlgFxA4sua1ZEGd0y2a| z*G(1>H`%;KeuFe-K=xRp2P8F9WsD8Usq%vjda87AF_|3WfSxK5@rN4U2 Date: Tue, 29 Oct 2024 20:52:09 +0100 Subject: [PATCH 10/20] add tss signature to whitelist spl mint --- cmd/zetae2e/config/local.yml | 4 +- cmd/zetae2e/config/localnet.yml | 4 +- contrib/localnet/solana/gateway.so | Bin 356384 -> 362992 bytes pkg/contracts/solana/gateway.json | 34 +++++++-- pkg/contracts/solana/gateway_message.go | 70 ++++++++++++++++++- pkg/contracts/solana/instruction.go | 35 +++++++++- zetaclient/chains/solana/signer/signer.go | 19 ++++- zetaclient/chains/solana/signer/whitelist.go | 34 +++++++++ 8 files changed, 187 insertions(+), 13 deletions(-) diff --git a/cmd/zetae2e/config/local.yml b/cmd/zetae2e/config/local.yml index f4a4456acd..2481ab31fa 100644 --- a/cmd/zetae2e/config/local.yml +++ b/cmd/zetae2e/config/local.yml @@ -76,8 +76,8 @@ policy_accounts: private_key: "0595CB0CD9BF5264A85A603EC8E43C30ADBB5FD2D9E2EF84C374EA4A65BB616C" observer_relayer_accounts: relayer_accounts: - - solana_address: "37yGiHAnLvWZUNVwu9esp74YQFqxU1qHCbABkDvRddUQ" - solana_private_key: "4yqSQxDeTBvn86BuxcN5jmZb2gaobFXrBqu8kiE9rZxNkVMe3LfXmFigRsU4sRp7vk4vVP1ZCFiejDKiXBNWvs2C" + - solana_address: "2qBVcNBZCubcnSR3NyCnFjCfkCVUB3G7ECPoaW5rxVjx" + solana_private_key: "3EMjCcCJg53fMEGVj13UPQpo6py9AKKyLE2qroR4yL1SvAN2tUznBvDKRYjntw7m6Jof1R2CSqjTddL27rEb6sFQ" - solana_address: "4kkCV8H38xirwQTkE5kL6FHNtYGHnMQQ7SkCjAxibHFK" solana_private_key: "5SSv7jWzamtjWNKGiKf3gvCPHcq9mE5x6LhYgzJCKNSxoQ83gFpmMgmg2JS2zdKcBEdwy7y9bvWgX4LBiUpvnrPf" rpcs: diff --git a/cmd/zetae2e/config/localnet.yml b/cmd/zetae2e/config/localnet.yml index 4a17827c94..5bd207a020 100644 --- a/cmd/zetae2e/config/localnet.yml +++ b/cmd/zetae2e/config/localnet.yml @@ -76,8 +76,8 @@ policy_accounts: private_key: "0595CB0CD9BF5264A85A603EC8E43C30ADBB5FD2D9E2EF84C374EA4A65BB616C" observer_relayer_accounts: relayer_accounts: - - solana_address: "37yGiHAnLvWZUNVwu9esp74YQFqxU1qHCbABkDvRddUQ" - solana_private_key: "4yqSQxDeTBvn86BuxcN5jmZb2gaobFXrBqu8kiE9rZxNkVMe3LfXmFigRsU4sRp7vk4vVP1ZCFiejDKiXBNWvs2C" + - solana_address: "2qBVcNBZCubcnSR3NyCnFjCfkCVUB3G7ECPoaW5rxVjx" + solana_private_key: "3EMjCcCJg53fMEGVj13UPQpo6py9AKKyLE2qroR4yL1SvAN2tUznBvDKRYjntw7m6Jof1R2CSqjTddL27rEb6sFQ" - solana_address: "4kkCV8H38xirwQTkE5kL6FHNtYGHnMQQ7SkCjAxibHFK" solana_private_key: "5SSv7jWzamtjWNKGiKf3gvCPHcq9mE5x6LhYgzJCKNSxoQ83gFpmMgmg2JS2zdKcBEdwy7y9bvWgX4LBiUpvnrPf" rpcs: diff --git a/contrib/localnet/solana/gateway.so b/contrib/localnet/solana/gateway.so index 6af69d7cf5e6ac6b3fd2a8aa849be21f8ef9eb74..3c9592ed62313ae3fb9de2f980a69e256a3ce955 100755 GIT binary patch delta 57850 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 Z?$%dxWBcUe=l9(a-Keu~d$!w^{{u7L2P*&o diff --git a/pkg/contracts/solana/gateway.json b/pkg/contracts/solana/gateway.json index 3f77bce291..801c2fbfcb 100644 --- a/pkg/contracts/solana/gateway.json +++ b/pkg/contracts/solana/gateway.json @@ -683,17 +683,41 @@ { "name": "authority", "writable": true, - "signer": true, - "relations": [ - "pda" - ] + "signer": true }, { "name": "system_program", "address": "11111111111111111111111111111111" } ], - "args": [] + "args": [ + { + "name": "signature", + "type": { + "array": [ + "u8", + 64 + ] + } + }, + { + "name": "recovery_id", + "type": "u8" + }, + { + "name": "message_hash", + "type": { + "array": [ + "u8", + 32 + ] + } + }, + { + "name": "nonce", + "type": "u64" + } + ] }, { "name": "withdraw", diff --git a/pkg/contracts/solana/gateway_message.go b/pkg/contracts/solana/gateway_message.go index 5c8b4ac49b..c6587e3ed7 100644 --- a/pkg/contracts/solana/gateway_message.go +++ b/pkg/contracts/solana/gateway_message.go @@ -113,11 +113,20 @@ type MsgWhitelist struct { // whitelistCandidate is the whitelist candidate whitelistCandidate solana.PublicKey whitelistEntry solana.PublicKey + + // chainID is the chain ID of Solana chain + chainID uint64 + + // Nonce is the nonce for the withdraw/withdraw_spl + nonce uint64 + + // signature is the signature of the message + signature [65]byte } // NewMsgWhitelist returns a new whitelist_spl_mint message -func NewMsgWhitelist(whitelistCandidate solana.PublicKey, whitelistEntry solana.PublicKey) *MsgWhitelist { - return &MsgWhitelist{whitelistCandidate: whitelistCandidate, whitelistEntry: whitelistEntry} +func NewMsgWhitelist(whitelistCandidate solana.PublicKey, whitelistEntry solana.PublicKey, chainID, nonce uint64) *MsgWhitelist { + return &MsgWhitelist{whitelistCandidate: whitelistCandidate, whitelistEntry: whitelistEntry, chainID: chainID, nonce: nonce} } // To returns the recipient address of the message @@ -128,3 +137,60 @@ func (msg *MsgWhitelist) WhitelistCandidate() solana.PublicKey { func (msg *MsgWhitelist) WhitelistEntry() solana.PublicKey { return msg.whitelistEntry } + +// ChainID returns the chain ID of the message +func (msg *MsgWhitelist) ChainID() uint64 { + return msg.chainID +} + +// Nonce returns the nonce of the message +func (msg *MsgWhitelist) Nonce() uint64 { + return msg.nonce +} + +// Hash packs the withdraw message and computes the hash +func (msg *MsgWhitelist) Hash() [32]byte { + var message []byte + buff := make([]byte, 8) + + message = append(message, []byte("whitelist_spl_mint")...) + + binary.BigEndian.PutUint64(buff, msg.chainID) + message = append(message, buff...) + + binary.BigEndian.PutUint64(buff, msg.nonce) + message = append(message, buff...) + + return crypto.Keccak256Hash(message) +} + +// SetSignature attaches the signature to the message +func (msg *MsgWhitelist) SetSignature(signature [65]byte) *MsgWhitelist { + msg.signature = signature + return msg +} + +// SigRSV returns the full 65-byte [R+S+V] signature +func (msg *MsgWhitelist) SigRSV() [65]byte { + return msg.signature +} + +// SigRS returns the 64-byte [R+S] core part of the signature +func (msg *MsgWhitelist) SigRS() [64]byte { + var sig [64]byte + copy(sig[:], msg.signature[:64]) + return sig +} + +// SigV returns the V part (recovery ID) of the signature +func (msg *MsgWhitelist) SigV() uint8 { + return msg.signature[64] +} + +// Signer returns the signer of the message +func (msg *MsgWhitelist) Signer() (common.Address, error) { + msgHash := msg.Hash() + msgSig := msg.SigRSV() + + return RecoverSigner(msgHash[:], msgSig[:]) +} diff --git a/pkg/contracts/solana/instruction.go b/pkg/contracts/solana/instruction.go index f69e7e8079..213489058f 100644 --- a/pkg/contracts/solana/instruction.go +++ b/pkg/contracts/solana/instruction.go @@ -117,10 +117,43 @@ func RecoverSigner(msgHash []byte, msgSig []byte) (signer common.Address, err er return crypto.PubkeyToAddress(*pubKey), nil } +var _ OutboundInstruction = (*WithdrawInstructionParams)(nil) + // WhitelistInstructionParams contains the parameters for a gateway whitelist_spl_mint instruction type WhitelistInstructionParams struct { - // Discriminator is the unique identifier for the whitelist_spl_mint instruction + // Discriminator is the unique identifier for the withdraw instruction Discriminator [8]byte + + // Signature is the ECDSA signature (by TSS) for the withdraw + Signature [64]byte + + // RecoveryID is the recovery ID used to recover the public key from ECDSA signature + RecoveryID uint8 + + // MessageHash is the hash of the message signed by TSS + MessageHash [32]byte + + // Nonce is the nonce for the withdraw + Nonce uint64 +} + +// Signer returns the signer of the signature contained +func (inst *WhitelistInstructionParams) Signer() (signer common.Address, err error) { + var signature [65]byte + copy(signature[:], inst.Signature[:64]) + signature[64] = inst.RecoveryID + + return RecoverSigner(inst.MessageHash[:], signature[:]) +} + +// GatewayNonce returns the nonce of the instruction +func (inst *WhitelistInstructionParams) GatewayNonce() uint64 { + return inst.Nonce +} + +// TokenAmount returns the amount of the instruction +func (inst *WhitelistInstructionParams) TokenAmount() uint64 { + return 0 } // ParseInstructionWhitelist tries to parse the instruction as a 'whitelist_spl_mint'. diff --git a/zetaclient/chains/solana/signer/signer.go b/zetaclient/chains/solana/signer/signer.go index 195ba3dc74..2fc4634788 100644 --- a/zetaclient/chains/solana/signer/signer.go +++ b/zetaclient/chains/solana/signer/signer.go @@ -151,8 +151,25 @@ func (signer *Signer) TryProcessOutbound( return } + // sign gateway withdraw message by TSS + msg, err := signer.SignMsgWhitelist(ctx, params, height, pk, whitelistEntryPDA) + if err != nil { + logger.Error(). + Err(err). + Msgf("TryProcessOutbound: SignMsgWithdraw error for chain %d nonce %d", chainID, nonce) + return + } + + // skip relaying the transaction if this signer hasn't set the relayer key + if !signer.HasRelayerKey() { + return + } + + // set relayer balance metrics + signer.SetRelayerBalanceMetrics(ctx) + // sign the withdraw transaction by relayer key - tx, err = signer.SignWhitelistTx(ctx, contracts.NewMsgWhitelist(pk, whitelistEntryPDA)) + tx, err = signer.SignWhitelistTx(ctx, msg) if err != nil { logger.Error(). Err(err). diff --git a/zetaclient/chains/solana/signer/whitelist.go b/zetaclient/chains/solana/signer/whitelist.go index 858308aeb3..2b4403cef7 100644 --- a/zetaclient/chains/solana/signer/whitelist.go +++ b/zetaclient/chains/solana/signer/whitelist.go @@ -9,8 +9,38 @@ import ( "github.com/near/borsh-go" contracts "github.com/zeta-chain/node/pkg/contracts/solana" + "github.com/zeta-chain/node/x/crosschain/types" ) +// SignMsgWithdraw signs a withdraw message (for gateway withdraw/withdraw_spl instruction) with TSS. +func (signer *Signer) SignMsgWhitelist( + ctx context.Context, + params *types.OutboundParams, + height uint64, + whitelistCandidate solana.PublicKey, + whitelistEntry solana.PublicKey, +) (*contracts.MsgWhitelist, error) { + chain := signer.Chain() + // #nosec G115 always positive + chainID := uint64(signer.Chain().ChainId) + nonce := params.TssNonce + + // prepare withdraw msg and compute hash + msg := contracts.NewMsgWhitelist(whitelistCandidate, whitelistEntry, chainID, nonce) + msgHash := msg.Hash() + + // sign the message with TSS to get an ECDSA signature. + // the produced signature is in the [R || S || V] format where V is 0 or 1. + signature, err := signer.TSS().Sign(ctx, msgHash[:], height, nonce, chain.ChainId, "") + if err != nil { + return nil, errors.Wrap(err, "Key-sign failed") + } + signer.Logger().Std.Info().Msgf("Key-sign succeed for chain %d nonce %d", chainID, nonce) + + // attach the signature and return + return msg.SetSignature(signature), nil +} + // SignWhitelistTx wraps the whitelist 'msg' into a Solana transaction and signs it with the relayer key. func (signer *Signer) SignWhitelistTx(ctx context.Context, msg *contracts.MsgWhitelist) (*solana.Transaction, error) { // create whitelist_spl_mint instruction with program call data @@ -18,6 +48,10 @@ func (signer *Signer) SignWhitelistTx(ctx context.Context, msg *contracts.MsgWhi var inst solana.GenericInstruction inst.DataBytes, err = borsh.Serialize(contracts.WhitelistInstructionParams{ Discriminator: contracts.DiscriminatorWhitelistSplMint(), + Signature: msg.SigRS(), + RecoveryID: msg.SigV(), + MessageHash: msg.Hash(), + Nonce: msg.Nonce(), }) if err != nil { return nil, errors.Wrap(err, "cannot serialize whitelist_spl_mint instruction") From 5e3ae717ff90121b58c224e0d21a807d87f2536b Mon Sep 17 00:00:00 2001 From: skosito Date: Wed, 30 Oct 2024 19:07:25 +0100 Subject: [PATCH 11/20] CI fixes --- pkg/contracts/solana/gateway_message.go | 13 ++++- pkg/contracts/solana/gateway_message_test.go | 2 +- .../keeper/msg_server_whitelist_erc20.go | 56 ++++++++++--------- x/crosschain/types/message_whitelist_erc20.go | 7 +-- .../types/message_whitelist_erc20_test.go | 4 +- ...axBorK9q1JFVbqnAvu9jXm6ertj7kT7HpYw1j.json | 10 ++-- 6 files changed, 51 insertions(+), 41 deletions(-) diff --git a/pkg/contracts/solana/gateway_message.go b/pkg/contracts/solana/gateway_message.go index c6587e3ed7..ac1d1886ad 100644 --- a/pkg/contracts/solana/gateway_message.go +++ b/pkg/contracts/solana/gateway_message.go @@ -125,8 +125,17 @@ type MsgWhitelist struct { } // NewMsgWhitelist returns a new whitelist_spl_mint message -func NewMsgWhitelist(whitelistCandidate solana.PublicKey, whitelistEntry solana.PublicKey, chainID, nonce uint64) *MsgWhitelist { - return &MsgWhitelist{whitelistCandidate: whitelistCandidate, whitelistEntry: whitelistEntry, chainID: chainID, nonce: nonce} +func NewMsgWhitelist( + whitelistCandidate solana.PublicKey, + whitelistEntry solana.PublicKey, + chainID, nonce uint64, +) *MsgWhitelist { + return &MsgWhitelist{ + whitelistCandidate: whitelistCandidate, + whitelistEntry: whitelistEntry, + chainID: chainID, + nonce: nonce, + } } // To returns the recipient address of the message diff --git a/pkg/contracts/solana/gateway_message_test.go b/pkg/contracts/solana/gateway_message_test.go index 20c4d84ef9..a7dd1e8a12 100644 --- a/pkg/contracts/solana/gateway_message_test.go +++ b/pkg/contracts/solana/gateway_message_test.go @@ -20,7 +20,7 @@ func Test_MsgWithdrawHash(t *testing.T) { amount := uint64(1336000) to := solana.MustPublicKeyFromBase58("37yGiHAnLvWZUNVwu9esp74YQFqxU1qHCbABkDvRddUQ") - wantHash := "a20cddb3f888f4064ced892a477101f45469a8c50f783b966d3fec2455887c05" + wantHash := "aa609ef9480303e8d743f6e36fe1bea0cc56b8d27dcbd8220846125c1181b681" wantHashBytes, err := hex.DecodeString(wantHash) require.NoError(t, err) diff --git a/x/crosschain/keeper/msg_server_whitelist_erc20.go b/x/crosschain/keeper/msg_server_whitelist_erc20.go index 7321fdb835..3746555ad7 100644 --- a/x/crosschain/keeper/msg_server_whitelist_erc20.go +++ b/x/crosschain/keeper/msg_server_whitelist_erc20.go @@ -6,6 +6,7 @@ import ( errorsmod "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ethcommon "github.com/ethereum/go-ethereum/common" "github.com/zeta-chain/node/pkg/coin" @@ -30,37 +31,38 @@ func (k msgServer) WhitelistERC20( return nil, errorsmod.Wrap(authoritytypes.ErrUnauthorized, err.Error()) } - // erc20Addr := ethcommon.HexToAddress(msg.Erc20Address) - // if erc20Addr == (ethcommon.Address{}) { - // return nil, errorsmod.Wrapf( - // sdkerrors.ErrInvalidAddress, - // "invalid ERC20 contract address (%s)", - // msg.Erc20Address, - // ) - // } - - // // check if the erc20 is already whitelisted - // foreignCoins := k.fungibleKeeper.GetAllForeignCoins(ctx) - // for _, fCoin := range foreignCoins { - // assetAddr := ethcommon.HexToAddress(fCoin.Asset) - // if assetAddr == erc20Addr && fCoin.ForeignChainId == msg.ChainId { - // return nil, errorsmod.Wrapf( - // fungibletypes.ErrForeignCoinAlreadyExist, - // "ERC20 contract address (%s) already whitelisted on chain (%d)", - // msg.Erc20Address, - // msg.ChainId, - // ) - // } - // } - - tss, found := k.zetaObserverKeeper.GetTSS(ctx) + chain, found := k.zetaObserverKeeper.GetSupportedChainFromChainID(ctx, msg.ChainId) if !found { - return nil, errorsmod.Wrapf(types.ErrCannotFindTSSKeys, "Cannot create new admin cmd of type whitelistERC20") + return nil, errorsmod.Wrapf(types.ErrInvalidChainID, "chain id (%d) not supported", msg.ChainId) } - chain, found := k.zetaObserverKeeper.GetSupportedChainFromChainID(ctx, msg.ChainId) + if chain.IsEVMChain() { + erc20Addr := ethcommon.HexToAddress(msg.Erc20Address) + if erc20Addr == (ethcommon.Address{}) { + return nil, errorsmod.Wrapf( + sdkerrors.ErrInvalidAddress, + "invalid ERC20 contract address (%s)", + msg.Erc20Address, + ) + } + } + + // check if the asset is already whitelisted + foreignCoins := k.fungibleKeeper.GetAllForeignCoins(ctx) + for _, fCoin := range foreignCoins { + if fCoin.Asset == msg.Erc20Address && fCoin.ForeignChainId == msg.ChainId { + return nil, errorsmod.Wrapf( + fungibletypes.ErrForeignCoinAlreadyExist, + "ERC20 contract address (%s) already whitelisted on chain (%d)", + msg.Erc20Address, + msg.ChainId, + ) + } + } + + tss, found := k.zetaObserverKeeper.GetTSS(ctx) if !found { - return nil, errorsmod.Wrapf(types.ErrInvalidChainID, "chain id (%d) not supported", msg.ChainId) + return nil, errorsmod.Wrapf(types.ErrCannotFindTSSKeys, "Cannot create new admin cmd of type whitelistERC20") } // use a temporary context for the zrc20 deployment diff --git a/x/crosschain/types/message_whitelist_erc20.go b/x/crosschain/types/message_whitelist_erc20.go index 4620e4be08..d27492d7a5 100644 --- a/x/crosschain/types/message_whitelist_erc20.go +++ b/x/crosschain/types/message_whitelist_erc20.go @@ -52,10 +52,9 @@ func (msg *MsgWhitelistERC20) ValidateBasic() error { if err != nil { return cosmoserrors.Wrapf(sdkerrors.ErrInvalidAddress, "invalid creator address (%s)", err) } - // check if the system contract address is valid - // if ethcommon.HexToAddress(msg.Erc20Address) == (ethcommon.Address{}) { - // return cosmoserrors.Wrapf(sdkerrors.ErrInvalidAddress, "invalid ERC20 contract address (%s)", msg.Erc20Address) - // } + if msg.Erc20Address == "" { + return cosmoserrors.Wrapf(sdkerrors.ErrInvalidAddress, "empty asset address") + } if msg.Decimals > 128 { return cosmoserrors.Wrapf(types.ErrInvalidDecimals, "invalid decimals (%d)", msg.Decimals) } diff --git a/x/crosschain/types/message_whitelist_erc20_test.go b/x/crosschain/types/message_whitelist_erc20_test.go index 8219140fd9..d5bf845272 100644 --- a/x/crosschain/types/message_whitelist_erc20_test.go +++ b/x/crosschain/types/message_whitelist_erc20_test.go @@ -32,10 +32,10 @@ func TestMsgWhitelistERC20_ValidateBasic(t *testing.T) { error: true, }, { - name: "invalid erc20", + name: "invalid asset", msg: types.NewMsgWhitelistERC20( sample.AccAddress(), - "0x0", + "", 1, "name", "symbol", diff --git a/zetaclient/testdata/solana/chain_901_inbound_tx_result_MS3MPLN7hkbyCZFwKqXcg8fmEvQMD74fN6Ps2LSWXJoRxPW5ehaxBorK9q1JFVbqnAvu9jXm6ertj7kT7HpYw1j.json b/zetaclient/testdata/solana/chain_901_inbound_tx_result_MS3MPLN7hkbyCZFwKqXcg8fmEvQMD74fN6Ps2LSWXJoRxPW5ehaxBorK9q1JFVbqnAvu9jXm6ertj7kT7HpYw1j.json index 210d639ead..cf7edb3b81 100644 --- a/zetaclient/testdata/solana/chain_901_inbound_tx_result_MS3MPLN7hkbyCZFwKqXcg8fmEvQMD74fN6Ps2LSWXJoRxPW5ehaxBorK9q1JFVbqnAvu9jXm6ertj7kT7HpYw1j.json +++ b/zetaclient/testdata/solana/chain_901_inbound_tx_result_MS3MPLN7hkbyCZFwKqXcg8fmEvQMD74fN6Ps2LSWXJoRxPW5ehaxBorK9q1JFVbqnAvu9jXm6ertj7kT7HpYw1j.json @@ -8,9 +8,9 @@ "message": { "accountKeys": [ "AS48jKNQsDGkEdDvfwu1QpqjtqbCadrAq9nGXjFmdX3Z", - "2f9SLuUNb7TNeM6gzBwT4ZjbL5ZyKzzHg1Ce9yiquEjj", + "9dcAyYG4bawApZocwZSyJBi9Mynf5EuKAJfifXdfkqik", "11111111111111111111111111111111", - "ZETAjseVjuFsxdRxo6MmTCvqFwb3ZHUx56Co3vCmGis" + "94U5AHQMKkV5txNJ17QPXWoh474PheGou6cNP2FEuL1d" ], "header": { "numRequiredSignatures": 1, @@ -47,13 +47,13 @@ "preTokenBalances": [], "postTokenBalances": [], "logMessages": [ - "Program ZETAjseVjuFsxdRxo6MmTCvqFwb3ZHUx56Co3vCmGis invoke [1]", + "Program 94U5AHQMKkV5txNJ17QPXWoh474PheGou6cNP2FEuL1d invoke [1]", "Program log: Instruction: Deposit", "Program 11111111111111111111111111111111 invoke [2]", "Program 11111111111111111111111111111111 success", "Program log: AS48jKNQsDGkEdDvfwu1QpqjtqbCadrAq9nGXjFmdX3Z deposits 100000 lamports to PDA", - "Program ZETAjseVjuFsxdRxo6MmTCvqFwb3ZHUx56Co3vCmGis consumed 17006 of 200000 compute units", - "Program ZETAjseVjuFsxdRxo6MmTCvqFwb3ZHUx56Co3vCmGis success" + "Program 94U5AHQMKkV5txNJ17QPXWoh474PheGou6cNP2FEuL1d consumed 17006 of 200000 compute units", + "Program 94U5AHQMKkV5txNJ17QPXWoh474PheGou6cNP2FEuL1d success" ], "status": { "Ok": null }, "rewards": [], From 4989775d15d4929b4b08c2b42025ee227bf2cf4b Mon Sep 17 00:00:00 2001 From: skosito Date: Wed, 30 Oct 2024 19:08:52 +0100 Subject: [PATCH 12/20] changelog --- changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/changelog.md b/changelog.md index 1d85c97f86..fb7a9108e6 100644 --- a/changelog.md +++ b/changelog.md @@ -22,6 +22,7 @@ * [2979](https://github.com/zeta-chain/node/pull/2979) - add fungible keeper ability to lock/unlock ZRC20 tokens * [3012](https://github.com/zeta-chain/node/pull/3012) - integrate authenticated calls erc20 smart contract functionality into protocol * [3025](https://github.com/zeta-chain/node/pull/3025) - standard memo for Bitcoin inbound +* [2984](https://github.com/zeta-chain/node/pull/2984) - whitelist spl tokens ### Refactor From 3b028507099d342717102bd3893383decd83b388 Mon Sep 17 00:00:00 2001 From: skosito Date: Wed, 30 Oct 2024 20:46:37 +0100 Subject: [PATCH 13/20] cleanup parse code instruction for whitelist --- e2e/e2etests/test_solana_whitelist_spl.go | 2 +- zetaclient/chains/solana/observer/outbound.go | 15 ++++----------- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/e2e/e2etests/test_solana_whitelist_spl.go b/e2e/e2etests/test_solana_whitelist_spl.go index 6c71c0d214..7ffe1bcb12 100644 --- a/e2e/e2etests/test_solana_whitelist_spl.go +++ b/e2e/e2etests/test_solana_whitelist_spl.go @@ -11,7 +11,7 @@ import ( crosschaintypes "github.com/zeta-chain/node/x/crosschain/types" ) -func TestSolanaWhitelistSPL(r *runner.E2ERunner, args []string) { +func TestSolanaWhitelistSPL(r *runner.E2ERunner, _ []string) { // Deploy a new SPL r.Logger.Info("Deploying new SPL") diff --git a/zetaclient/chains/solana/observer/outbound.go b/zetaclient/chains/solana/observer/outbound.go index 0bd2399619..60bd70bec7 100644 --- a/zetaclient/chains/solana/observer/outbound.go +++ b/zetaclient/chains/solana/observer/outbound.go @@ -155,11 +155,9 @@ func (ob *Observer) VoteOutboundIfConfirmed(ctx context.Context, cctx *crosschai return false, errors.Wrapf(err, "ParseGatewayInstruction error for sig %s", txSig) } - outboundAmount := new(big.Int).SetUint64(0) - if inst != nil { - // the amount and status of the outbound - outboundAmount = new(big.Int).SetUint64(inst.TokenAmount()) - } + // the amount and status of the outbound + outboundAmount := new(big.Int).SetUint64(inst.TokenAmount()) + // status was already verified as successful in CheckFinalizedTx outboundStatus := chains.ReceiveStatus_success @@ -298,9 +296,6 @@ func (ob *Observer) CheckFinalizedTx( logger.Error().Err(err).Msg("ParseGatewayInstruction error") return nil, false } - if inst == nil { - return txResult, true - } txNonce := inst.GatewayNonce() @@ -359,10 +354,8 @@ func ParseGatewayInstruction( switch coinType { case coin.CoinType_Gas: return contracts.ParseInstructionWithdraw(instruction) - // for these currently parsing is not needed since instructions are empty, and these instructions dont implement OutboundInstruction interface - // can be revisited as more are added case coin.CoinType_Cmd: - return nil, nil + return contracts.ParseInstructionWhitelist(instruction) default: return nil, fmt.Errorf("unsupported outbound coin type %s", coinType) } From 66d04bc5a8def70c73086a696ea6c5482566c89f Mon Sep 17 00:00:00 2001 From: skosito Date: Thu, 31 Oct 2024 15:26:02 +0100 Subject: [PATCH 14/20] cleanup --- e2e/runner/solana.go | 1 + zetaclient/chains/solana/signer/whitelist.go | 4 ++-- zetaclient/testutils/constant.go | 2 ++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/e2e/runner/solana.go b/e2e/runner/solana.go index 5ba3dcb92f..dede911181 100644 --- a/e2e/runner/solana.go +++ b/e2e/runner/solana.go @@ -101,6 +101,7 @@ func (r *E2ERunner) DeploySPL(privateKey *solana.PrivateKey) *solana.Wallet { lamport, err := r.SolanaClient.GetMinimumBalanceForRentExemption(r.Ctx, token.MINT_SIZE, rpc.CommitmentFinalized) require.NoError(r, err) + // to deploy new spl token, create account instruction and initialize mint instruction have to be in the same transaction tokenAccount := solana.NewWallet() createAccountInstruction := system.NewCreateAccountInstruction( lamport, diff --git a/zetaclient/chains/solana/signer/whitelist.go b/zetaclient/chains/solana/signer/whitelist.go index 2b4403cef7..dd917b36d2 100644 --- a/zetaclient/chains/solana/signer/whitelist.go +++ b/zetaclient/chains/solana/signer/whitelist.go @@ -12,7 +12,7 @@ import ( "github.com/zeta-chain/node/x/crosschain/types" ) -// SignMsgWithdraw signs a withdraw message (for gateway withdraw/withdraw_spl instruction) with TSS. +// SignMsgWhitelist signs a whitelist message (for gateway whitelist_spl_mint instruction) with TSS. func (signer *Signer) SignMsgWhitelist( ctx context.Context, params *types.OutboundParams, @@ -25,7 +25,7 @@ func (signer *Signer) SignMsgWhitelist( chainID := uint64(signer.Chain().ChainId) nonce := params.TssNonce - // prepare withdraw msg and compute hash + // prepare whitelist msg and compute hash msg := contracts.NewMsgWhitelist(whitelistCandidate, whitelistEntry, chainID, nonce) msgHash := msg.Hash() diff --git a/zetaclient/testutils/constant.go b/zetaclient/testutils/constant.go index c4af08d900..f776c7019f 100644 --- a/zetaclient/testutils/constant.go +++ b/zetaclient/testutils/constant.go @@ -42,6 +42,8 @@ const ( // GatewayAddresses contains constants gateway addresses for testing var GatewayAddresses = map[int64]string{ // Gateway address on Solana devnet + // NOTE: currently different deployer key pair is used for development compared to live networks + // as live networks key pair is sensitive information at this point, can be unified once we have deployments completed chains.SolanaDevnet.ChainId: "94U5AHQMKkV5txNJ17QPXWoh474PheGou6cNP2FEuL1d", } From 28bcc5fe13b680da9867f0852b929d014af3952b Mon Sep 17 00:00:00 2001 From: skosito Date: Thu, 31 Oct 2024 15:43:43 +0100 Subject: [PATCH 15/20] cleanup comments --- pkg/contracts/solana/gateway_message.go | 2 +- pkg/contracts/solana/instruction.go | 8 ++++---- zetaclient/chains/solana/signer/signer.go | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pkg/contracts/solana/gateway_message.go b/pkg/contracts/solana/gateway_message.go index ac1d1886ad..80c50fe46e 100644 --- a/pkg/contracts/solana/gateway_message.go +++ b/pkg/contracts/solana/gateway_message.go @@ -157,7 +157,7 @@ func (msg *MsgWhitelist) Nonce() uint64 { return msg.nonce } -// Hash packs the withdraw message and computes the hash +// Hash packs the whitelist message and computes the hash func (msg *MsgWhitelist) Hash() [32]byte { var message []byte buff := make([]byte, 8) diff --git a/pkg/contracts/solana/instruction.go b/pkg/contracts/solana/instruction.go index 213489058f..c0956263ae 100644 --- a/pkg/contracts/solana/instruction.go +++ b/pkg/contracts/solana/instruction.go @@ -117,14 +117,14 @@ func RecoverSigner(msgHash []byte, msgSig []byte) (signer common.Address, err er return crypto.PubkeyToAddress(*pubKey), nil } -var _ OutboundInstruction = (*WithdrawInstructionParams)(nil) +var _ OutboundInstruction = (*WhitelistInstructionParams)(nil) // WhitelistInstructionParams contains the parameters for a gateway whitelist_spl_mint instruction type WhitelistInstructionParams struct { - // Discriminator is the unique identifier for the withdraw instruction + // Discriminator is the unique identifier for the whitelist instruction Discriminator [8]byte - // Signature is the ECDSA signature (by TSS) for the withdraw + // Signature is the ECDSA signature (by TSS) for the whitelist Signature [64]byte // RecoveryID is the recovery ID used to recover the public key from ECDSA signature @@ -133,7 +133,7 @@ type WhitelistInstructionParams struct { // MessageHash is the hash of the message signed by TSS MessageHash [32]byte - // Nonce is the nonce for the withdraw + // Nonce is the nonce for the whitelist Nonce uint64 } diff --git a/zetaclient/chains/solana/signer/signer.go b/zetaclient/chains/solana/signer/signer.go index 2fc4634788..a86b7eb659 100644 --- a/zetaclient/chains/solana/signer/signer.go +++ b/zetaclient/chains/solana/signer/signer.go @@ -151,12 +151,12 @@ func (signer *Signer) TryProcessOutbound( return } - // sign gateway withdraw message by TSS + // sign gateway whitelist message by TSS msg, err := signer.SignMsgWhitelist(ctx, params, height, pk, whitelistEntryPDA) if err != nil { logger.Error(). Err(err). - Msgf("TryProcessOutbound: SignMsgWithdraw error for chain %d nonce %d", chainID, nonce) + Msgf("TryProcessOutbound: SignMsgWhitelist error for chain %d nonce %d", chainID, nonce) return } @@ -168,7 +168,7 @@ func (signer *Signer) TryProcessOutbound( // set relayer balance metrics signer.SetRelayerBalanceMetrics(ctx) - // sign the withdraw transaction by relayer key + // sign the whitelist transaction by relayer key tx, err = signer.SignWhitelistTx(ctx, msg) if err != nil { logger.Error(). From fcbaefba917a30f2029412b24e69b64aba1c234a Mon Sep 17 00:00:00 2001 From: skosito Date: Thu, 31 Oct 2024 21:43:26 +0100 Subject: [PATCH 16/20] add whitelist candidate to msg hash --- contrib/localnet/solana/gateway.so | Bin 362992 -> 366416 bytes pkg/contracts/solana/gateway.json | 34 ++++++++++++++++++++---- pkg/contracts/solana/gateway_message.go | 2 ++ 3 files changed, 31 insertions(+), 5 deletions(-) diff --git a/contrib/localnet/solana/gateway.so b/contrib/localnet/solana/gateway.so index 3c9592ed62313ae3fb9de2f980a69e256a3ce955..0fe82f24f22d1d765fb38561a8c2c37fc75b629b 100755 GIT binary patch delta 55059 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 delta 54594 zcmcG13tSe}*7xi^1EL}V3L@$wUP7`&(j+uRx=7>&sT8RcF%wbq5z`Q&fJ%ujA~wmQ zjuLo5Dt)9#hEkEdAes{1kW7&*kxUUQiB5&z+B5T9X4`o?@Av(_=XZ{a|5|IWz4qE` zUuMsxT8nq9?49G|nY^C}>=&l~D+nd8uN@lvK+Hi&#a1e~YhtGBjQ`K5(&Rk#nX2je ztS#&{F~ZwPMLhLeswr8i)i^~qGoI(Am9tZslE%9lM-_rbI75Jc)AP2k(+mQ5zY!ahwg*UE~>%=Ph#yUA)%BNS?$)Qex zv}v7ecIu#0kvQkmPc`N?B%NYu0cv*2RLc)>nbk>JBM4r?Ao(lW`@i*|B1>*?(L6v4 z^kQ~tZf9>oUwA>bbcv>gFUS>c7CL&qZ0?>(1Ln)2uF*8)1vwYVvoFXku4VL(cvW^K zUJi7NrvF|pXSxN`p<20pn(UK!EO(du`>5Aw*>X88*jwK19!C2vkvrV$Xl}e*=V76H zv7o1!dQX!9jTShl8lY@Il(=KK?yLTO3IZuxF zwb1R$WbZy^`qOkdvricfm?ww!4W`={mnQX%Ce&k@T+=U_u2`&AdgEDEDfL|@Tl$B3 z)^fGZdt0rw@L5(XH7%A~{OahmXG^OFgc7>akysoP%xS8cV3t@l!A8z64UZ}!bkR$4%VV*g zi5>Qdf@Sc)7vO5L8o;W(IS4V0rkmgLFP0tWKh=Tih>9ZP=vvC^35>5^Ae+haM~FTG*3UHk@gd?*IeCv=zQrOB4)cy-3=HDfw0z!_pj^nW{9 zolTQFX4nfSx%_Vmm!!#&PlgXXp*G$g3{TGC&^MrBs1!c%Jg2vp)4PIG_fy`UIPa@* zwT9mF6*=;$5fFBl)TQ*znXi-5&NIL2L>E_sd5Y-mSKNhuPrjT4jPtIq|| z)-2hyIG$D?k~0_kQ<_q0S!^Qoi!8au>`y;WmfOrGT9sVty(E&**Rx9F6N5!MI9bkL z=1oT=EBW0-HzFTc9z%a!q4H?59Q;Dy(6(xOYATrU-O(ww3900guzyBr_6w0@;IS3j z>~RZ!J~%s6bE*Boa=9ZZntt_qY4nOrLf?N~Zb;7bOyH~qzal54l+no7O9NlTMr2gU zk*VH8GdQi#7wvWJ9jrym2RB^(56ju9fi&!}T#@Qc?>#KnrJCr^X{BwcxERklEJwZ^ z?|at~W4m1ScDW03&C9{`%wgF%EtI}}L=I02rt1&OX=$oK3lg6ras!g4BeGk1D4mS< z(!Gbaa=u-}9&{2OQJva5!0y}Q=v%r!MHkDEpl=OGwqtD>Cc5{q?4RLJXC9Fwk)$1w zlaQ=9EaxM6;M-<-`Rk4Jo;W#eb+o)KD}#PKLk@f+n0Akov$Olaho@+FxiNDvbz;g- z$?k9dO23_r%8$!2Z%w8%XUIiw1=Gsea>HAZv}|^1;Ma1B>I2$dQX=hwp zkrb_z&1?K=;YvAoO)wp}NUm8EN`nO1Ehm?5S}qsm1bc>Ie62q=M)Ghd<%H5VqvgQ0 z&&!RQ4$zs)ON%zg6FPjk>{e7kdo7o1iY%1i?s2Py{8T!u_yQq2Kl!zE)u(4D?det;yAub( z3J9q%)?Jrt4%*DUbVc^AiX=DXc~uJi{VUmY$eSiy zmxB)l(o@G}(_wEq^@^N!D4y(=FCCgqPRbLi*ZfD*_gt4-s%-^*uE@cML;s_4zw2`L zVVfV)mD0MyCKqhb&Etoxj{JH}t~+5<`LWVcI!v^RQt$$MZBv5zn|=kwajmdHt${OKZW$E8p@f2Q1U$(ueqQ*OHyt8Z3% zd2kinfqVBRe;jpNn#R-cnQ{|?U*v9=XOpkw#h0&>tEID=J4q}~@mKxnD^JVWS3~Kf z$K;x;;n?3^*VfR7rpQItOk=W++7nd4IX@T^)Cbom-bc$~`x?R4ql%+)$2IK6qjF$N zHf=j9SG2_cCuh5w<;d%~bkWmt)Ae8v-tty?SnH76xVz+etut_l?`a)BaN04uk8JP^ zeM}G(^x#ZjN1ODkG;G8_{=N+^i*QcLD z=_`|E(=Wla*AH^^FR^-;4v6}PF5N6&`(?JZ|K0yVdF^63^H&p1IEJz8EARXDDZ28Q zY`PgvFbFpvxh*s|FU3ICL5wN0hYFUpoS{{enn;Fd&bfjgoHZj*eo zZHBu2pZePr4_>=b-u_$v|MqHNz+$k}(S>qi`@<}7W$mG~Y7)d|(1Ehg?>*_v@8!VX zEA)U&{HK7Nf6kt4tfTM#4@o(9nH>4=V7lR$ocZruPk!2-gQp;Wc;CjJ8862M#>>0^ zaHn0S$u)mekkZoCf4**1a*|yd^md%{6!K?j~|0Hkp$k>#HsV3LL^D#FJ+WO?jxiY z7CVvobU-rHIgtul+Zl459jvdu1TD@a(AvV7ixAui57REQeCz{Xb|O7)tKrDAEQm1K zg}Bq?$CX$Y65olef`i@35^@?wxs$!L)dkwz)mqdA=z$*~(!<__uHQkv2Z^U*4NmBN zlmVWkRJtvWSKyL2Nv9B{#P=deloq@I4ZZP?wJ#{qzT^sVWyx{tmLM$hZT<_!_9cmA z2W;<4Mv*A^zAt(7*?V6Qgw*%lJ6KnyzUiXUl;VWH7@qs0q~f^?1(hx>{88mE^Zi}N z%{p$^$`!V&IMugR$E_-cg1MsC$c|gLQhgg$LF$|JD&1Sis=@2YE9g-|xrW=65W5 zCKr_U^bBDxIk*_t0r__k?~s3Et#gV)un+LG)5TX{=d^PQ$ErBhH%v8{QW&P=5DkAA zaSOH`B!2SUPA(xOx+_h~Szl%7B{a=c-(;%=mCp5DuF*FWJ7DG?#BX#W3*9nSyVf9= zJ6RN>v{D-*RO+O9#rP4bA1NErALzE6^o8DckwLVw3PSI~w3>YAyg9N^G)8-qs6zk^tY_37o+2CxK?aQZ}z(iTMUn$6>?;yr>H8qga;N0QLnYPG~c z_DJ;VsYvj?7e~~VWlGw;#F^0G&X76EVVJvo2Mwd_HJvyQfx%dlxker4YlF#Mx7iMl zgPLG;{?{oGejh2L3wuMyeK=j+u?*5e5PNrq;L%uxa7b&+-0vWJG)C<1V^A>~8#n7b z)Q!d}t@5bvz<&&4x8o4Y8vp2dNE(9^*_$;`Glp2`4_6@Vev(g{e84o81mEV5Je#%k zUU+{jp81cT0xe_Fnb)5O^ElE*y}CersDm?m8lfW8!5QToIFEO5=A%Z4X4vY?rgKm< zp2X7aHMp7aRs6!pIq5bJ(qh4F0=a!vNr{zB_s)k66G(SDWU^wJfIUJ3mqKs^hOhrp z#S(#CLp%2d?@7qmpQvO{B5s6kc7goK_DS-N256gXpNU(~Lg*9>&pvIE{A3Eb%jq_c z_RWNr$I)iuL?!+Sl1*vS5@?%_jQL9xGrK85C;C7`3M7z(J>9vD!ssi|Fq1^GtpN<5MW)=Q@bXWPJ&Ra3RR4HurD6^=&BFd|KLWY2*xb2c zP#24#PbA5eMP(a0Rwj{K9w$k0+_4p429= z)QQ?my!Sp0@Ac7Wioc8chxK}s9nSnxBR8z|_Rqt8IlKDw!~f%vkxHivZul3@cXP@$ z>LGi+6Y+4O3i=I*P$@fv!i5R1E231Kic>24BPGwJRi$%qr-HwJuYgL~ra$6qXxK#j z<(J-b=^4hQ%;{$Ah{k_SR=1PG*H7`}6?$KR__s-ty!(AWx(KiR-#4+1V#oWT>7B>3 zxw%B2vx_xathwVPJg!!sGACK18(UTREYF?VTs|j5=eO(pPgOo^9{Z)t$n*NPw<&!6Beptc{ePW)?s4F@hM47P8~o_24ngpj zcW?0V*cr+Kp)Y|Oap{v+Z|dWkvR2zSr268bJ8dP2NcTj4+3KDC!>wB>o2s>dZ7f%* z^^mOlJ!-QA1s8rZF0G3mzykR5LA-qSvPAaxbF&vONPR%dEyiDwumCj$Vcv5KmZd(x z{?hg%tiIGSdOd}e{k4jEs&b+Ky&6qfkfZ)_t}}S9A|pn}Gv^8`4bEAewVpC|*UP59 ztJye*zJ$TcnWPVdT@!o4-K&U?6peDVTBV}V8h;Na! z?t0F6k#VY?+NC|YJiT$*n5Tnzg;Ch0Uiu9gCt;qNEpqGnXXW)D_|fNWeb(?n=!18J zvi>U2o0z9kCt-Ae!XK-xl0SM}7_2 zTlFzEmh&BW-+6^^F<VT^Dms{5X$`|hO{Ntsh1ciq&V z6vJ2V;C^UMg8c2-J0S50amigrI`{KVP@BN^k!%Rz_DU_qW(e(sxo?KU6)$V(-dOu~ z6Yz9wy+;h!wBfLLKAEa6ID6-lDYwa&LB9lIy&C<;3;56Ag#@yOY=9{#SO{lZa|-c? z$qR@Nc~Y6TfDCcMqY)+BOo9nn4F{KyB{bv+1SjHZnMa@~k>rvT7`K!x@r>g9ymZO9 zAbIc#cf#W5ZI1rq!nkl2$EAT&hx&$`7N)C~qjiK%~hK2fmAS=lxdh`Mmt;9`e`2`4Hh1;g$3sAKR zw{vhoiOj@(6^*!{RAu4rg=~Sp-XvQ+`5C0>BGe@5n-%i}z+ZyaZC#yphYtW%g_-hT> z>Nq2L@)M50IpV=9JPhmB5+Ghy!;$Y`65JaC+3yfDnFfEoL&lTWV4O@op#4IiLB=?{ zhCuwgB$8gJg|c^XL~3)2M>RL6T!ks?@Tg?Nc`)VTb=7O^<83ypsJsiInK7b4o)#29|se-3rN855C)efPS0x@w8+!jwfCo055FBri_7Zo3v3H zxCz_h6AWfXBQAhtldanVJ^1a z+YeqvWDVZt0rMfe#@$y$UL>ExxUFQW-j{*5b<-fDcelys!^_3k_J@qM&HNB+o1wh$ zAvN9p#e%7e(@gR8b z#I^HFyoB3{X|d}pm>DiR3%NUSY^{aUJFy`jomJX)V#Da`XTeQD`KxD@V1-?y#hz87 z%h`4R=(9@N=j@N3XO*Ha*dN!wQEGOPd^{*r7Vp7z^^jYI@^6knLlq9aX-6RN z5GHj9>K(#s%C&V$$036E8pqc|lm$_+Swg3QPKm~ax0AU7#HPvQt5pTUGWGF(igPB1%+dcoUuB!ldQZfD4BI`Ryd z&tRKBsa0=@H64PQGdR1u*F$(cHoa#EWY*(pfKsa+{!|`~F9^ zH(~oXUI$ZkUAcXZ`KINyZI z>zVSy@aSc-h?OY2Ov3Sq{IAQ{^rk8ZZpMDnLtb+bvYSafiG`cZlI~iB6V9r5k`w7>V&cl?n zt2p(Lg>d?sUEJ>=c(iA+nrc< z0HS}!yqf`DzmOS3fyKYro3-$OV)=!g3pPCo`CVvVIPoiahNj&Mp@*oy)uVe3K+;W| z2IrrInw$2);d=lA+t7zsPJ+43?!U_csA$84!nN%;?8FaGLhNri4Y?nO?BDQo@2CAx z^c$8Ma}t_{MzH{G}&T7Jg?8uTPYx>FNu`vdPi41E$TH!&#FPD0tg(O>IX-pl*J`wvv^`Xprk zfi5{c2^C1mR=D}mE&CGUyV43e z>Q3-?rAat}ZE(fHddffiB{aBVy{}@SKrCdff7F){-3=SF2&da__JL#i65QOdF{^4J zl2P4C*Ds;SjmEP%vO675zeH(wl!jp9b;nfssS=91(;~7ArnuALWHG$#PKS^L*n)^Y z6AjtjSPN_M{uDLigg(y$*=P4ct_QZtnts`pknM?%K6@NON1)A>mC)dcHa~+^J+L`5 z^sJm;2~|C4HvRQ$i0+9l;5wDd=&@R;V>GE2yuFZGNArkEFneinF7u)$k_e~0=w&hm z_IRs7g8b{G7pa5}Z(2pZgoC~4eYX1#=l6oQ54yDYI7IrOODFe2Jj2!za(FM4`OpTu zxLMcR-d$VvDs{cFon$vm=!0x)ke2L)%s#Y?ju--=eQ79J2QT;4T!_7iF6>n*`ch|t zWBXt~x`sRj(@hSAyJ56Uv<#P$DgCKJb11OZQ2%k@??;=cI2_ytVDI9cla>XTdle8l z0Nb+yyav)4=h)tp^cs9 zC*;5}CfW6%3mhFxUC2e{^kBM|;KI1hAB|XV**H}|oj(pe>y6aGUqa{*9BO(VUEc$l zLokAPe?q;_dhtuBV`6KJ&g}tH0Lrb!C+q_=!&dXvd!QnKCeeUn5PT={b+bG608X`i zVdI_jc{=6^hsnL}C`1p%bkK*=o2WPx>%&pkFch=lEBI?DW5Ed}Bn5drwziEXI-vH`u_4Q`{b zNy1>L8HEx#~_^1?vH`_evBl}{;^^7B)m74K8+Ld&9U_M zjnwEN#*Nfs*glT-@6B(dq=VW<$^@5C%=S^vp*TT*4)a3k*SJp_H69zi;xJ^7$NHZ? z3}r~kD{y{1&Sfvb;`>qRbX-Aia9d68fV2m&ZTINvckAl+YwE!^_3Q_kde9wUc?d(` zvKyKnqS2n;RpWMPDGQ?($e}?H`7jNZ>X9$!^zTc@1VIrpNV{_WVXR)=jNtnaBp&d! zh~I4M-tV77@FO&y-ZK>PA3?9J>){WdL&GC9lf13Wi@*thT!TH2YT2M}6be3vjz?(~ z9Xu7PCbFKG3T+cH({UV3Qd3-g^5W^w!GAIv2LqsKGL0rL!?-D`6ioO~BE~Ik3VLXU zos4VE(E*=B+Z5VF-dD~?(nkoDhJ$|;dU#2F#{xH#TcU6*EroH9;UxEya_liWo7mi5 zQ4THhXdjsK1RZ3X1M+r3{uAh=K04=?Lv}pw-CU;8q162^Sl>c?0?OH4=~wOX?2K_t zNzretY_*?>!?_mX=b7Me51Sps>rs0uq8v+Y)$W6&6l%xE)5!?+&^7ga7xfOmEZDl7C+jizk$Q>l|sqP8KWP&@li z^=($4T(BPNRD6JwHzMZU0l=w!tsG7F)#bz=UCOHZb>3VTaA`>M=1s2Jw%c zuR+gK^cxyW3njJObmFSEAZ3#lKgX5_{DqhK7&%KV{)6{&@ zQn$3K`m9Bpu|*3Com9^j`Sw-m(!x$E zUFK_2X-Z+7O7S@e9Vh5me-5EAQO8lbJXyyvI?m9spJphfFiU3y=n6SH4pQly!aSAE z_0=B8a10Tj96>C^dMGi}u;@3^yGsnVlEqOUJ?TvfQ;O4W5){U$bUFbo1IJXWvHG7X z>BaKC*q-k!*wbmO){li+9;A4vT_B`*YIi$RhiXws@pK=hN>YbvVM_7T?o_1?)xu~k zi1)?RRVabZEtivN9*1#WeK}y*)I8662 zM5T@e0`z{@XP)%+YQG2R`T-iXwxp-tPZRZq^wb+NQEy03y_+WfhlUht4Z$&|*)7x} zYJGeP?*waip>Vd*6|^paoF~Pe0Y2#J-py*0g-)2NEU#G`WBapC^wC}R>ww*<)Cacx zj`M2-EApCFgk`tB+elAhtt^hxD~V947s3^Sy+HmXV*+X^2nutz9AI-5~Yvc~AEp^PZ2%M)`V%D<69EN&t zshJt2Ptp-8bu18~PwbeOY~Ps9daN)+R}WLElkm9~VcWfCZF-pHc}3^zdAU&w*euV> zD&O8idK@-s^Ukb6->Y&j;-|L9(VcXbZR|Wb!{Hdx`Xq(pl(HPBF8h>HZ*U~lw^qkm zDy8~X>sTAm>0-aX&y^TuOp?DJb3)HZ;w>U=#KwgL2`%C~oq9zELtx}@9D^4g08 zSi4F7ekSfiN2!jx>yv?tN*(*LU3XmD!=*Quv1Lxs)wRbwZR79%dAiej(>~p4 zy=|ZFYWe9-Yp^=qr8lFee>dHIWMT={tcSW)5A`H9G)o__P9vMm)KkYK>R6xp$0TT2FPA!0v(s;Y_F$8B z%J3o&z{mJZeV5~U!nONDO)OqF=>77s+Ap@S|e~2=~!z7jv^g5 z>W$Gxmwi)aU4ArQ?fs#(1y`SLx&f`RT7Q)PhyE~|Sbvo3u2$;((Os?DG4rypyln== z=$&9s?QY7^%Vnw5Q9s$1skm@yJKOYCEG&h|dYKHpEC_f4fSTdGFBpNhK=9xm;aEQ}t(GQVO*x zIMq8&mkXLb@AMd*(X1D6*Rgh~ld*C7`&pQqtFrM5rAp00 z$DAnUa%h#BLnT_b^xIgh{^3|4PfsG;);CO{y?Uwnlc(u#EKsSFP}-r~JC_zv+tqKw zPImRHzJw4MkHGj^*fi}9=_Za?^&X&t-w)nTRv_^HA}F@{&;+!NL%RmkI&kAeU5H1tmXq-9tIrX=PuF5JR0Uv0oJJPB4m zub#Xpmgn)LnilK>|K&LS{A(XXE>{gh=nFKTF4zYRFChO}mG2LCC((MW6`WV#ncA!} z$XtQm-ib;pu-yapDW+svLu>(gx)hvWM0c(2pIi!QFJfS<_`XubKb1OD8{?NxA^c_9 zLF0}=INlu69*Rn%Qy9Z79giNg51RD&o7b3NemY%6r{T@XWq4NvkCfap@OZLNd(ANG zQzbuxI(K4kroh(M=>!^30xhr8_#XSdxOL0=Q37_i6B|EWxcl zthPW>0s7pj0vZbNfVFK8#1|s{U=OquqR;wUnv*S%wh=Q*e@pZM3)F4ILp!VI_gTPy z6E$14{Vn(a3ZCcdg|l#cCqglp9++y-?! zFc`X9?`%^dOIWh&IC2}9KgA(tt?s^U;9rX7w(WwlQp~&PT@d^kHu{m>U}n@hX-M0k z;xn8?^yXds7;1Jp#HRjZFqNTqy7Y0jxq}yTKZdH$ zF&HPmfV3~LL(;y0wlDC$hF-qk$I!ORA%}W?457R2wTT}qRlD(k(yD&?BPF=PG1f&N zDVDv|BHHfo4f_a^_G6M-?}oX31m*)+Z;1XL#h)KS#{qWq`2|!QL~4z~;Sce-4O&Cb z>&de&WN8@zpet*eZzs5G;o=MXeP?ehB6xsA#Pq@I%G?6%BQ=S$eG)s%kNy z*J_oHTAC)Yftq$&8>lkAEy!?q-TypJKd7TS*&Gyo26O#~<4|!1SCPW4EH4Kt?PuuZ z&LU1U@W^*KoZAKPzDRRPci4Urk6=Z7_JGc(ZRf#q5hHg6#x)^4uR(hJJmmj?;n}C- zvh(2fJ=XD|uD(H2&(YyZaJr18SLkpN9J@@n(&+P$Hd+j4x89oR5;7i!{fH;(t;Zqr zM{KunNU8V{^N*fC2ccJS;MAXk_^UXG51vyx5ED`W*ILlicVSfvosZ9Ds3;YtTt{S9 z5q;(yq+O-naCa+Bz&lf_MmXGTr7KuH<{LO%upY||>`{8=TQL8M^_=(?a(~5o_I|4b z-o!4%8*sDQu%6YhrwuJvo`coGX{GG%= zQlLzC5<5{cTbb2K+#%8*>%pz77|R}iGItf@X>C1N9+1W;?ylm)B8{tu*iNFi@?3YZ z+J!uN?5!x<7S+E!?WuC;Rx`OVK#?rKtT81n+ zsU{Geds54gXdO?)XAdsp-P}>|@@Vl9diP1FXvX_uCKxqFTtRD3D)@PjHhhH=wuFjP z*+Vy>;&67eW4x%4sml5B;s+uPJ*9pD!{xNLV`KX@w(oUC%HHyhlhiL@e0ECx0>*;V z>K8ClPN`qO=yytHU%E1L$g4_uhpy8*~FI@b2QvJe(2dd&1 zF5X0I878{^l=_8>(WlifTs(bR{ldkxQ|cEk;!mkxxafIW{lZ1SY4r;i$4;qVxcCmM zcm=Is0VE}-m6}JyHi z5Q-0@RZPPMR(}mm)5Lgu#&61WaR@1c7p99}`^LE0!)ZPHWLuKv5{Qi!@!^SJ$c`4n zso(^$Pm11fHCoIgI2>k({b^<$m}iUraCC;~PT#77h8ftL7&kD-U~|wwW{hT_EC#Eu zEWx*;5Xan!&vS^8G|UO#juM>-=Ye)rse6$2lxD#46dDM)6Y8GAf(5!KW1ki)X{!@- zJdN(5Fnp$%?^|7?Z9Q3br23YqdwFI|aE6YVVh#Bij?EIMQg>%C#fs6)siat}qjeM% z#fn7)=abpCh6U?wGQok_nEp{}8E@ztC&sV>NpU!S(13+eY*ka7SVc>m!7@iP?EDN4 z@zzly?1&TZfSJ!=jL=K-GpL1L2hY{KPMa&{OWz&Cf#f$=>`Bqj;Ah1edf*s1&l6)w zIn0}fomC2}=ZWK)_w&R@$VTvr7q_x0DjpF|xAVpO*zLLbVt;&&e%5@!u0`8x9*R9YwwQa(3}PZ07bbW6mVC&95qtT`Np zEf;%%>r$LdMr!(Gh;nVIID^oOUqMYI*7)2qtnmi~iC81v^bTAu=F+2IX*E`U1uaX_ z_@gg~g9v@FUNI-(OhJFISK?n3@!{~9XOxDQMBFEJJEK&jVZ`Y2ZxmC8h+CefGm8JK zVkn_SsQj9kO=#bGrEMkCDfmW-&J>&Q&g>Z_G)wdrX>*+t{T6m49qsa)YzT9KG)4=$K-K$VC@ts&&i_Ix zbb)Y2Yh56d(V(tS^)EJecY+Q^OR)TUq!}(?X0)*j6fxS`2^tuU>H^*ykotATeKOLF zu8_%SZ6~N;G^h)-F)DP1zOm3Wfy2Fz^)cLfm zsjZ&}CSp4nt;E+j8LjOMMU3XS;3MQn z3-HZ*Mq9gpX$w*TOW}BDSB=pw8gNzRVMb?PoNnE5tHdfc?X$ zAGV*-jLzzZe4?=V43>0JKjagJt!H2EDd?%`VCC*&F#wai z8w7riQ?k+w<`0p&cZ1l^MH6iPPz>_j)NEe>*m~NTWh=6_HRFYdm`P_IgQSlzOCLT4 z`S(iR@~QRi%GHm=98p?#0H0QI3_3b~(Pp0r{=GVJ0CEuV;G#2vPLD*i@q?1%hPT=cH(hl)~EyNnMi zmf~*Y!hUqeZ+cJKxf#Mg6VvFY_#|N{dcJ=@w1wiPYNIk?r+8VUt*%-)PN?DNMbv17 z`&M#+=%xqaSTj_7j-xu!6@tGI^J(v9uNP8!L$pf{0+_Oej>*evUj1^ z|Wg z!L$eUb1p+<9ZnzJl*~P1tTV0b22K01`uc7VdO&QTt=$yUL9tH4uQl8}EG}`Pv6X-u z0dM7{uSFL^$5gUu-lS}<6@5fz%zPS+xk24Y^eyKy`(PQ3a|5?i=wh-P1fR0Ab8uzX zpyq)a6xpQAM1wA@VD7nr_i204RyT;WLm0abJM-PsxFYH``gMm2yPSq}hc*Wo)g1!s z?8XwhL%ajb=nnZhv~21Hi%;N1%@1{|ZRj|qy5e@mUOLDP!q4E6){b#MgZ)#C7nf(S z8(rMMzaD4jNM&a|?mFpT6^i++=O z+Bd#*yHV~C>?TE%6lJxWlup>b)6HFqrlIB9)x=BgQUM{IlzyJla8(-FLkgw8D3IMl zGSjaVrK5+0&BbEQUJ`E0P~aw+l;vI$rWtb6yroFCiS?GwAXJKaNhXQY{fOkJBn7(lpA{ib16J#=a;Tlvh+T|x`>n8m+_2P;rkG?)!m*oK4lv`V19x3``@Wc=lLi<_HWD(;tCBe!m6W zMj}Qzo4Of4b|hwo+yZHg#(6;gNbJkCsCO^w_0{zR)Vmiu!=YRm%GtAp_evkI;@P9n zT$l$Gjlw=If}5kH`Sf5d#0De#&N|rbSrc)`b`U%#vrY}jvuW= zI`KMwGy~~J7%fJ3wLm_j_^5Hk7;NWf*P)K#fvwQSXkaUN-;dP)Is`L1@;XE_x(xki zG_4hK8U3>b${2OQ@{C@&4sDFKT?g;6NatgDMpLl-SnSoDR;U<@sb1g#&f}zTY1gOv zI#=1MepOR>X`GaR&$EB39TANCR6QafUa6!CrLrHdYs0L^Hh-8AbMUtVXvKi4(;*@ zFh@z=zK;CvtJE$OeDIDA%JXlNhv{-Ob@_x+6NS^l{}01%)1)Fo6TF~ox@4hYpFn&x zwlC2WDxz^Lox1^nGjQx>V0&jsvFc^-49t4e^Nx|yXfwv=S;<@379;r(D}r-RN(sIt zp7v1V?NRlH8oPGI(8fN6b0pHbr2BKUsi0b>M4vsT1kb_oihn@nGg5_b#|?Yby$HKogME$!pJJYUMEU4I6* z1RN)5%|AgZqOLzfO@dU0R^-Uv@O_dT$rVZ#NXvBtec*wG(pPj5*0~59=Z01mp;hKf zyoAq$J}1ev`_Eumg}`I6w8>znak2CqvlD5SGO4@jfG@O~rJl@Z=OxGtvZbjy4q|#J zO-m#<+yRY+w&&5?oUuyoGReO?*9>39{Wa+neyUn^s}GP>(nGp$10i9R^f3MQ2Ju|@7FPWh4&V$g#rzfyV4B!NDO=5w2RX58B!BFGJY`vntu5%G zb)~dcim@>(dqBln?02hDqptMXJDQT31VT^6EMq0&JHJTEnRQaPuDZ`Xkt$# zyI8_c=Ab9)k(Y}X1b>W^gr670>ooUcsYsmKCc*e^(l~MCO$pMsNnU;4x@m8dlfbs- zTB>IJF2N3_>CrC1b<`B+v`b2-?b6G5+K&l{%O0#Rk$h+v#=ZnsW*gV_Q;qBYsT8hq z&86rGs^ypJ47Ko|pJ{5|J8=PIg(J%}wX`ywp{gBKG_@v0%2n0UK9}0i8YF#z11Ioj z2;PN}LTcWH0YKWZ3p)*I{BEg=g*0#vPWj@m|CV6tUTIwq{6lS{+D`n334Yrv?Gcmz zkU*)F4$>$uNc$393Gq@aUrIlF5J7qGq*Ng(y}rSO1*n&1DLG^obaCLs zA=aMg)0mES`eI<%bvgUJhk66GKiR+O!Y*X6&s$r6M-bx@YWw|(*x!bje_-K1OHzWg@m#%9xJj-nTh~&wubO8;mcyI<|HEabrsz4NYUqjb5cAf7XBC$2ssR zZW?+x=9(8Wp11ig2zb*c(%s41_wJMSu0D>Txl7l<+Yo6?9*S~y8-e|X;{UY=Ed?PI z?_QwXsrXJ&@PW0iPCdF>2MMy^>wZq&4|9VjL)6xAzhPPzgy}SBh=#5F5S`y_GuQZcI`CarV=Nf2tq@hn1hTG}3=C_>cBd&>?nRrTs9w(~*IX%h`Tw5j%`O zf%EtuD=5YC-ZpE3@IOWb>;GR@a2PA-?@+=2OGH@x!nJQCby!$!_BtyGgRKDR{fM;( z4K?QTh#e=At%&b&(62{)w*!{Gm4xvQ_#wplhpN>2g%!wf9Ov&M9^g=?1$=Op0{Rj$=9gd#(Gwo@yWY*Wzi2E=UQu>KAqb~M<8*fD2Z&KvPi z#Eub-M65cX*DowXhIN%>l@uTzZmkXJ*NE{MT-&b=v7-ZC4aV{hB6jSe6^I?XWE0{C z(3-V=;Rj?m_Mz7WNx0uuoghRZ9`AtHAP#ZBhY&yLfIBxzLbwB-idgS+mHQgvP>1@3 zFOf09p@8#ul5nR39*%gN1AY#1umj$R*wMl75IZ)=^P;gqQxH$Iw;yL>vN4Dq9ef&be+T^+ z5j&PIs4~u+AEDeaLO&u-H#r#Sbjesj0AfdZIAX^>d=ar@j_gG2n6y72c1*(FO~!d7 zs!5t}NRnJs(B8(;y!smPSO*8YUY7L#bcM!z9I<0g97k;3jy*>N+ji^tWia)|#n4fn zfO6|%iSkI4_j8oTqTFgLYKv`!cHgQk#8@OBi--|h^c6beUz)(m=+?O2#t>X!shd+z zSG$VN>9WD5qpkC;b`_1$xY?-DEkG|4gFb0{mTo26G@XJXQ3z1CF#~Hq)>w`$dJKbR z-5H(1n?Jr7kxO_p!nzw`!&Q4%Mi4IHJ&!(k72qhJi*l=O^-q%41^U0A{s^(C@B8VG z_U!`e0d2D+2q@3y<&F_Xc?B;Y;?M&qZ{g*`jdD|8uKp0CJd&5=HIuD}(S8~)pNw+r z)&}Jklsl>IgF-*29u!#($P#Odv0`R*qB1%YE;6!g=Oj|s0t-JHC- zwFhVnKP;dpZxsH?elAGewLN&UUJP04Am|pMGlU>(u?e;JYsHesYP5E^M!&;jP7l?f z_Rm4Y)~mV!cx_=#$abc$ZMuWdD*N4m$8h~|OZ*(3npv+F7+*$=C2cIn}!RG=rsRevQ^;FGd z`818z&e5pH&-Uxn1?ZeL^^2*#5nHcb7@m*VI-Ma*7Z!1G1w1Bmy!43RE?Zyc9NL2Vj{Q=E z*g6(jg`XgHyw1K^D+$`m2TZzH5YC{2{nA1Z{>k8Z)OTz#um20mtr5r9&21YvH#{_( zI?&0h5B{O=V+D1jTf?yf50lcF0{&s!2+u?(t+ky&g&n9MvOf+DbhhD*8lFPHDJ~W$~xXkdTD=IC!`aad8pf5GljQx%zClh!oze@|YZ7bN>pASc!6=X5a3Y8EI9$o$W+Q$=aL+eZ5W?XE z4!3VJ>Q`?!Vs{IS8Rle4k0>$n6HAS_oWu2>8RdREjX0;wi0e5Vq!{GsLb8z|Bv%>~ zgh~##@8{$Pj5y|?5xX2R;sQMZw4Q|cKqt@ib^{N;+i=oY#*f1>9Cklt)DPgW-&vzP z<68qx7xIh@p`63b98NxGFd#H?xShlG=Z*UA4UQPE1~hH{ zkKu3@hYL7d!r{awn?1FDq4=_mq5e_H;aU#2b2zNoSiYXa!jDFI?N3%bMEk?!!GVdc}Nc<&f;)0hZDVx`sM0##(INT zvbgj%DkN~Ygu@B!n4gtX|HUW1I4txt%A+`3>44Q_jp%PIkT<}H8#x>_&?wK~a65-v z*(I2R!#)8vtky5ovTHC$g&-r&;&AOKqr8$`gE^GP|3-)bCvv#JhPC>IavMV}CscDd z>j9%d;Xxyg;Be(bM!Da^Mx1EHdi_Fym7&!ylykV6!&wuI2K^o};tUSga#)Da^wX_L zB8MwE z+{$5>CynKOI9$)+u-Oj!EI*s$92kgw<`{7phhsP#_l!}$lEdzEjq)%Dtd5Tu2L?-# z1P;eNYc!BJ--v5D?3ZAa$8fmZj_nx7E(9Gee z4MuqhhlPBjyxM^86@=suj0!FrjW~$I$s6I#F?c~-zuBl)u+xZJIqX+vl>3z%aU6&9 zb{XX^`wci<@G~-mFb>CXIN*@MfRM=HgleNa@30Yv9dX1sd=tKMWC%iHtq~WWFyexf zM%>6@pHoJ8Nu3cV)H`4{eFS{tz(Cymtr6#(Gvbm4Bd)w)#8Hh#9QU0Ompfo}`f9ys zREYS&h~1lvxSGQ;myPnUW+N{D(TM%7*swNzX4x5R?i5OXvMH#z`l=DTUo+yc79$R5 zHR7xrMqGT;inaO$zcwpFtxu@uaKdj!d3C!H2mEftG5TKN%28dKGX~oGLj+2aX7iSIImX8FTVKtD~$ylJo#fXK@MjXZA#4bj; zpQ{nabW^c?`3UN6R48{h;${ycj_6^;)f`UjWt7)**pEG+%kE2CTb!se?6{o6F1?Kg zV>s;RYm`UyG2$E!SN26Y>wl{Q5&ew?Dmk3R?yETHH*&ajpiv$(*oceoFk<|#)@%3W ztqv3qH7X?EZNy>tx`?9#85}O>u=_nm{jiZnTpmod{I@z#d!JDuImC$b#u#zsSR?kq zcRU>(%;9j@14en{L+M6F$iqgQ!Qq$*MtLiTo5PLrga{5lYQ*K~6OD}KNk&{e#fT#! zjW~I#5ywOsahU$tvNghqPZ;It_0x@v;us?qo;2bJ4wpY=lovd0#FaCRxRyPTh(Fz- zbfI>Rfg!ZcHRASXjW}eU5od7NJ;5liUtq+Q&pG13f)KLUk%5m1a#%=&i4WsWDQ79C z$6@#9jq(5vhj6%&!!gT@<&$4D;B=wV$PhwO4GMyLh7sp**yRNe^Se>aN*4PQ9Y%R;i4iA!YQ#ZhMjW6xV(f%+0|Ot}``l3hr;{%@yvv9ab{lcP z9u8L+amIcJtPTQ42KGW!wSxlUY7YAxHp(M79LM2A4hI~umsgW8$AQ7Jz~_`*f#GHj z$DA|Db2uFKgHfKr;nrVla%~U<{Ay#c`h*A$C;w)Y*K)X>$8OpKMb<^qy}J>&dRehn zzu@9+WvKNFVH_5Gjq(KjA#!U4S^5*{R-B z&fstfhZ{NU9%8f?rDOZ$0;iC};c^Z)a@b|Gv4Q{&M{zh=!}iOC0#2ct!_6Fa8Dp$4 zh{I7FPUdidiZTAyKFS+!G+4snurQ-MhQmP*807&E8*v1OeI7+Q>wl{QL5$&m<2YQ* z;pB-%{Spp`O)|=3IGlxeierT(oI)dqgC-jdhDT8jZ(tO2>~;>rBMwNjV^+Tqmtkix zT*=|Ql}33xhnrU!<@K3HoR?+9iErAlTEEb2XR!M51MfBk6}oe{{B5J$WwjAUaX62| z^){^4FZg5|4a9IbkHfVbc3ERAAI9Ni4wqQ5Ucb<6WvJ~H1iu`k!59wbaJZVo!dheb z5Dq8m*tVn;a|(?d_Ibx>Fp9%j9IoVWJBI^g)t-GxiP0Fg2{wzviup7{TF04(D;Wl3_R><)SuS^YwJnNh)AG2&JZ=TsTx#T>5Ya3hCZ4%y19^$P)Z2J=S< z<8T~@lR2En;Svtla=6)swfY5@YMV!@1AZJ1<8TazlR2Ej;Svu2pRVpTW{#?i1NeCh z1Jjn^zEE%>L6)GhRG|X`E|S=Zs96x{XrK&K=>`P1w6+6cTt3K-;$<))jzBqEiMR=Z z3wT)s?FKb&yiWPz#z?aG#St|wF(IP~$rdm5oSF0N%=3Rgbo+arbDPV~zVFV=nS^Zm z7YhpH-K&3&z<|Ilun!KvF?eLW`rJ>TKnfNw@&VPsAvggq!QJ2I?UkSVVG&q;40jj2 z!w?*U2e0z-<2ShHVDWFASL>e+EQ|l}3gW-q4R8xQ{~s@(fosQjzNvk!8UqbrERN5& zJ~;*3Ymnc--8kWRxoekoR^Qvm6TMB`gOj-zVCPLdKLRhnjT$T87d{V&7@UBYV0WE0 zC~Bv0x4<1{wM`=yP~Vw@=U{Ov@1P0xz&-G=YW<#0ssZH;T;IYwbitmv*NpZB@2$Li z4;)vVA5-R)fZnSzu>UsRU<^*d^|$l#4%jRGN!_0a%0TWETt5#Q1P9;<+-b_KH>(pu z&iTBZHaG%L!5O%@mAB`EBksPK@PJr=>+j$Vw!uB{2s{Vd@2s|`Hg<~zgz&*Va10)U zXW$H6yPz@vy|X#hfU*Y;z%h6PPQVMWePKC(d}nV~0(xg_gS+4eJOU@+1=!xU8bIE^ zXqExpzlgzeaLZXWke8ob!Y#}#s`U16f}?j+`4jW{VkwXA*0)KtC|-o;=6+FX$2K=u zl2dTwy}Z5$9)XwKec^5A4LDbFyI>z2fFtk_9DK0azSI<@az&5Mk|3vl;Cy!_xA?)rzh9q@?zO3}QQSBSt%aB~MQAMTJZ-fF$& zV*h#`ukYk`!0AqT<|kCWme0#gz|M_4Z{H;EQ1$xhO+3B?4|nmr|1s_*IQ=-!yEk*! zcQf}z!UH1eumYlCZXTw$&j>sRcW&YJWAKDo?Y+ruyh8VO?)Wp@33&2Zp0D?~LvUKP zK9t$_@Cx(Kal7|&Mcn0$wq zTYwu+@w^9)p62-p*!wQex4*}HrLe!x0>TB)z^xzf@?tDs*rONZhdiEuqvv?ud!9Q4 zt2q$!ZAmM%3l6yZBH{rNg9qRtcmy7UC*Ucw+7Z>0o!E#|@DePJ@$xp99(G4-pb2@W zYW+rNR|Co&a2Fhc)kE^w0*t^@@Vw&ujhI#fx}^+ko5?+BiE7{`*a5p>zjSdR=$3(e zNqXQ2JOGct6L12auUzym7OOzszgU86YgV41HP8e*;5OI;cgd!I5m2D$UxeTRcm$q+ z6L1R7j8|XE3G_WS!@}j4axoMbf+ye^cmZC5Z8N_lEnp+JTK^Pi zK>-ik1=EC;RDS^ZA$S6wYU}M!4J1%t2A+dc@B*BHm*ARu%1*xj(E#e^u{-1jm>$bR zc^C3+a0lE2hsRsR{!a}KpuiA30nfk-u(0^swgzrkeO|zU0xsACcfbKS1jpcE9|B|W z6g&f`;0!EI;0ss-_Ztv!z%JMWcfbKS1jpQcG2{U;22a5=a0<@A;zU>j%xeE@uz>oC z3G9MBa0eWKLvUQR-v5WyfbtkT1<$}KI0FkC)#<6=+7Sq?1DXT2ONMya9p~0 z{}0Q6?q7_-Q}7I&f-|t#2y0;FqJPm?1@iud19rh4xC0KrAvh+R{>6|2MgL+9o`PrK z6r6#@CRhW;tIz)i6mY;U*aLUK0XPK5h0D+XAru&cr{Ebl1!rKf8P-5h9awKZQ}zR%jW7Rx>R zth3JJTCxUPcR#W7BN)2=_?^iGD_Q`ye z*5wfuzNii14!8%7<>mXWZL+stEo>$assFFFnJr&p4p6iM;B78pT5H zIbdx+$2D^*QXvoAl}A+Rb((sSiVfwfs;%_|O3&oRK!q0&&gA(jEPH{~kdr_S)Rjk6 zV67*Q25Kk+dB&aA_VvU(@`d`C$s2XJZXQoUVMiXi)B2iPjPviZcCR(%{dcJ*TJn&7 z@#PWy5*y3wPtM+{xc6)(7v!-zx$2-`avZb&LCWX+Wu&wubDs zdEPU#vrs;`f#wSdjam5nL20y{-=5Qq`z`M-|1}qC@{p%%OY^Ols4BXwx^8wg1_vKLsO>-L^ zIWc#^kr(Esv`u>Z>j6y7U1wAv0oz~a1E_(==AJC7??1=OcfgUkVW&?O#7n$FZOQGU z-{AR9z8K{u?19rYy_jWwS-8L2=H3QgV6=(b+05O16ZgEv-C5^uoX+j%R_mYEK>bW! zU~nF{)8rmp%smHtm+*WSJiAoo_4cO$EOema#M~W1`M~A*nYkN;^6vY2z5{N3faeos z-T!I;on5?uy_>ssD|h>L?xwlmod%SEN9O8H$}b;3F7JOe0LNTiNd&aCO`g_Yhd^cuMn7v4{1O%bL}CyX|6RS+Y??t{3*Bi8Mo8_IS;hJonP>L^h@rk zxmb|~umm^EwS$!R!TlHl30Ne&!xlIIkH9IoZm!fb15)SUc|b(qDR>EX%%ys?gadPB z9(lCj3y?6Y{cp|!>T=H4c!dc#{3p-PU+1ppm+F=KRNY*8M+5Sz*89JGj5pwd8|F$n zY9Io4jw`;rr+n`O?g4mNabI`s*}Ouskvlw-yKXM`q7J9$^8CVF{6zWG%q&jMN*AC1 zfw|y>3iQAsI08?=33z%DAK-jtwf^-QXuXRU@WAe+Jl_S+FXQ=!x#EQusMX?m_X=bE zlmk70=nDDA2h_Q=csnmRY;(`d1sv3H$6QT8&cKPec!KiN9nhXRr*HCok(pEIRKdTO zR~VX8=#+1pQ|RR7eZ2h4oFS+D$eaNuhvp1;VYNAu2YCa{N4P_9V9roegMm3iO`d}r zkMZ`rs`cA3tOk^a=KL--SbKsu5IxB)M%-O<-j?dm%z0XJ$DEhd&VMKAn)9JlVDP+r zAy)fF?N_RV`q6RSMNs+6NPAZKOD7+KnckJZ?0U$_JEi7!SNG3KQS|@>Gt}1 z6*Ug?0?CAXY^F}50nE)zXJqH<^0P*_7lRY<b)%w>XPIgkD@c0I9zjEx?<(G%`zr`ld-!(bg%kp}H{Nazvymsv;WnN#d zIR3H8>+P=tvAHx~uTuSW%;0EQK_AZBua?$jdX?V?^bGvdtjwRJUHQ1z&s3OfDF>t{ z*%ueyeOvEtaiN?&Yz;U1zb>-s85Ad3TjT==ty9;--xM)={hfUFptWr?T}Po_ Date: Mon, 4 Nov 2024 02:34:27 +0100 Subject: [PATCH 17/20] PR comments --- changelog.md | 5 +- cmd/zetae2e/local/local.go | 2 +- e2e/e2etests/e2etests.go | 3 +- pkg/chains/chain.go | 4 + proto/zetachain/zetacore/crosschain/tx.proto | 1 + .../zetachain/zetacore/crosschain/tx_pb.d.ts | 2 + .../keeper/msg_server_whitelist_erc20.go | 21 +- .../keeper/msg_server_whitelist_erc20_test.go | 202 ++++++++++++------ x/crosschain/types/tx.pb.go | 1 + zetaclient/chains/solana/signer/signer.go | 172 ++++++++------- 10 files changed, 267 insertions(+), 146 deletions(-) diff --git a/changelog.md b/changelog.md index c4959e1729..e2bcc9196a 100644 --- a/changelog.md +++ b/changelog.md @@ -2,6 +2,10 @@ ## Unreleased +### Features + +* [2984](https://github.com/zeta-chain/node/pull/2984) - add Whitelist message ability to whitelist SPL tokens on Solana + ## v21.0.0 ### Features @@ -27,7 +31,6 @@ * [3028](https://github.com/zeta-chain/node/pull/3028) - whitelist connection gater * [3019](https://github.com/zeta-chain/node/pull/3019) - add ditribute functions to staking precompile * [3020](https://github.com/zeta-chain/node/pull/3020) - add support for TON withdrawals -* [2984](https://github.com/zeta-chain/node/pull/2984) - whitelist spl tokens ### Refactor diff --git a/cmd/zetae2e/local/local.go b/cmd/zetae2e/local/local.go index f4a51179f5..79bc07528a 100644 --- a/cmd/zetae2e/local/local.go +++ b/cmd/zetae2e/local/local.go @@ -372,6 +372,7 @@ func localE2ETest(cmd *cobra.Command, _ []string) { e2etests.TestCriticalAdminTransactionsName, e2etests.TestPauseERC20CustodyName, e2etests.TestMigrateERC20CustodyFundsName, + e2etests.TestSolanaWhitelistSPLName, // Test the rate limiter functionalities // this test is currently incomplete and takes 10m to run @@ -405,7 +406,6 @@ func localE2ETest(cmd *cobra.Command, _ []string) { e2etests.TestSolanaDepositAndCallRefundName, e2etests.TestSolanaDepositRestrictedName, e2etests.TestSolanaWithdrawRestrictedName, - e2etests.TestSolanaWhitelistSPLName, } eg.Go(solanaTestRoutine(conf, deployerRunner, verbose, solanaTests...)) } diff --git a/e2e/e2etests/e2etests.go b/e2e/e2etests/e2etests.go index a45afb3caa..5f6adf9b87 100644 --- a/e2e/e2etests/e2etests.go +++ b/e2e/e2etests/e2etests.go @@ -61,7 +61,7 @@ const ( TestSolanaDepositAndCallRefundName = "solana_deposit_and_call_refund" TestSolanaDepositRestrictedName = "solana_deposit_restricted" TestSolanaWithdrawRestrictedName = "solana_withdraw_restricted" - TestSolanaWhitelistSPLName = "solana_whitelist_spl" + /** * TON tests */ @@ -130,6 +130,7 @@ const ( TestPauseERC20CustodyName = "pause_erc20_custody" TestMigrateERC20CustodyFundsName = "migrate_erc20_custody_funds" TestMigrateTSSName = "migrate_TSS" + TestSolanaWhitelistSPLName = "solana_whitelist_spl" /* V2 smart contract tests diff --git a/pkg/chains/chain.go b/pkg/chains/chain.go index 0102e74517..8d1b3c25c3 100644 --- a/pkg/chains/chain.go +++ b/pkg/chains/chain.go @@ -96,6 +96,10 @@ func (chain Chain) IsEVMChain() bool { return chain.Vm == Vm_evm } +func (chain Chain) IsSVMChain() bool { + return chain.Vm == Vm_svm +} + func (chain Chain) IsBitcoinChain() bool { return chain.Consensus == Consensus_bitcoin } diff --git a/proto/zetachain/zetacore/crosschain/tx.proto b/proto/zetachain/zetacore/crosschain/tx.proto index cb76e53d0a..ed8b6b6f59 100644 --- a/proto/zetachain/zetacore/crosschain/tx.proto +++ b/proto/zetachain/zetacore/crosschain/tx.proto @@ -71,6 +71,7 @@ message MsgAddInboundTracker { } message MsgAddInboundTrackerResponse {} +// TODO: https://github.com/zeta-chain/node/issues/3083 message MsgWhitelistERC20 { string creator = 1; string erc20_address = 2; diff --git a/typescript/zetachain/zetacore/crosschain/tx_pb.d.ts b/typescript/zetachain/zetacore/crosschain/tx_pb.d.ts index f830e00a48..db3e7073ea 100644 --- a/typescript/zetachain/zetacore/crosschain/tx_pb.d.ts +++ b/typescript/zetachain/zetacore/crosschain/tx_pb.d.ts @@ -186,6 +186,8 @@ export declare class MsgAddInboundTrackerResponse extends Message { diff --git a/x/crosschain/keeper/msg_server_whitelist_erc20.go b/x/crosschain/keeper/msg_server_whitelist_erc20.go index 3746555ad7..979ecc2850 100644 --- a/x/crosschain/keeper/msg_server_whitelist_erc20.go +++ b/x/crosschain/keeper/msg_server_whitelist_erc20.go @@ -8,6 +8,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ethcommon "github.com/ethereum/go-ethereum/common" + "github.com/gagliardetto/solana-go" "github.com/zeta-chain/node/pkg/coin" authoritytypes "github.com/zeta-chain/node/x/authority/types" @@ -36,7 +37,8 @@ func (k msgServer) WhitelistERC20( return nil, errorsmod.Wrapf(types.ErrInvalidChainID, "chain id (%d) not supported", msg.ChainId) } - if chain.IsEVMChain() { + switch { + case chain.IsEVMChain(): erc20Addr := ethcommon.HexToAddress(msg.Erc20Address) if erc20Addr == (ethcommon.Address{}) { return nil, errorsmod.Wrapf( @@ -45,6 +47,23 @@ func (k msgServer) WhitelistERC20( msg.Erc20Address, ) } + + case chain.IsSVMChain(): + _, err := solana.PublicKeyFromBase58(msg.Erc20Address) + if err != nil { + return nil, errorsmod.Wrapf( + sdkerrors.ErrInvalidAddress, + "invalid solana contract address (%s)", + msg.Erc20Address, + ) + } + + default: + return nil, errorsmod.Wrapf( + sdkerrors.ErrInvalidChainID, + "whitelist for chain id (%d) not supported", + msg.ChainId, + ) } // check if the asset is already whitelisted diff --git a/x/crosschain/keeper/msg_server_whitelist_erc20_test.go b/x/crosschain/keeper/msg_server_whitelist_erc20_test.go index 3eb18b9931..c82261bd05 100644 --- a/x/crosschain/keeper/msg_server_whitelist_erc20_test.go +++ b/x/crosschain/keeper/msg_server_whitelist_erc20_test.go @@ -18,81 +18,156 @@ import ( ) func TestKeeper_WhitelistERC20(t *testing.T) { - t.Run("can deploy and whitelist an erc20", func(t *testing.T) { - k, ctx, sdkk, zk := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + tests := []struct { + name string + tokenAddress string + secondTokenAddress string + chainID int64 + }{ + { + name: "can deploy and whitelist an erc20", + tokenAddress: sample.EthAddress().Hex(), + secondTokenAddress: sample.EthAddress().Hex(), + chainID: getValidEthChainID(), + }, + { + name: "can deploy and whitelist a spl", + tokenAddress: sample.SolanaAddress(t), + secondTokenAddress: sample.SolanaAddress(t), + chainID: getValidSolanaChainID(), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + k, ctx, sdkk, zk := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseAuthorityMock: true, + }) + + msgServer := crosschainkeeper.NewMsgServerImpl(*k) + k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) + + setSupportedChain(ctx, zk, tt.chainID) + + admin := sample.AccAddress() + authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) + + deploySystemContracts(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper) + setupGasCoin(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper, tt.chainID, "foobar", "FOOBAR") + k.GetObserverKeeper().SetTssAndUpdateNonce(ctx, sample.Tss()) + k.SetGasPrice(ctx, types.GasPrice{ + ChainId: tt.chainID, + MedianIndex: 0, + Prices: []uint64{1}, + }) + + msg := types.MsgWhitelistERC20{ + Creator: admin, + Erc20Address: tt.tokenAddress, + ChainId: tt.chainID, + Name: "foo", + Symbol: "FOO", + Decimals: 18, + GasLimit: 100000, + } + keepertest.MockCheckAuthorization(&authorityMock.Mock, &msg, nil) + res, err := msgServer.WhitelistERC20(ctx, &msg) + require.NoError(t, err) + require.NotNil(t, res) + zrc20 := res.Zrc20Address + cctxIndex := res.CctxIndex + + // check zrc20 and cctx created + assertContractDeployment(t, sdkk.EvmKeeper, ctx, ethcommon.HexToAddress(zrc20)) + fc, found := zk.FungibleKeeper.GetForeignCoins(ctx, zrc20) + require.True(t, found) + require.EqualValues(t, "foo", fc.Name) + require.EqualValues(t, tt.tokenAddress, fc.Asset) + cctx, found := k.GetCrossChainTx(ctx, cctxIndex) + require.True(t, found) + require.EqualValues( + t, + fmt.Sprintf("%s:%s", constant.CmdWhitelistERC20, tt.tokenAddress), + cctx.RelayedMessage, + ) + + // check gas limit is set + gasLimit, err := zk.FungibleKeeper.QueryGasLimit(ctx, ethcommon.HexToAddress(zrc20)) + require.NoError(t, err) + require.Equal(t, uint64(100000), gasLimit.Uint64()) + + msgNew := types.MsgWhitelistERC20{ + Creator: admin, + Erc20Address: tt.secondTokenAddress, + ChainId: tt.chainID, + Name: "bar", + Symbol: "BAR", + Decimals: 18, + GasLimit: 100000, + } + keepertest.MockCheckAuthorization(&authorityMock.Mock, &msgNew, nil) + + // Ensure that whitelist a new erc20 create a cctx with a different index + res, err = msgServer.WhitelistERC20(ctx, &msgNew) + require.NoError(t, err) + require.NotNil(t, res) + require.NotEqual(t, cctxIndex, res.CctxIndex) + }) + } + + t.Run("should fail if not authorized", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ UseAuthorityMock: true, }) msgServer := crosschainkeeper.NewMsgServerImpl(*k) k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) - chainID := getValidEthChainID() - setSupportedChain(ctx, zk, chainID) - admin := sample.AccAddress() - erc20Address := sample.EthAddress().Hex() authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) - deploySystemContracts(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper) - setupGasCoin(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper, chainID, "foobar", "FOOBAR") - k.GetObserverKeeper().SetTssAndUpdateNonce(ctx, sample.Tss()) - k.SetGasPrice(ctx, types.GasPrice{ - ChainId: chainID, - MedianIndex: 0, - Prices: []uint64{1}, - }) - msg := types.MsgWhitelistERC20{ Creator: admin, - Erc20Address: erc20Address, - ChainId: chainID, + Erc20Address: sample.EthAddress().Hex(), + ChainId: getValidEthChainID(), Name: "foo", Symbol: "FOO", Decimals: 18, GasLimit: 100000, } - keepertest.MockCheckAuthorization(&authorityMock.Mock, &msg, nil) - res, err := msgServer.WhitelistERC20(ctx, &msg) - require.NoError(t, err) - require.NotNil(t, res) - zrc20 := res.Zrc20Address - cctxIndex := res.CctxIndex - - // check zrc20 and cctx created - assertContractDeployment(t, sdkk.EvmKeeper, ctx, ethcommon.HexToAddress(zrc20)) - fc, found := zk.FungibleKeeper.GetForeignCoins(ctx, zrc20) - require.True(t, found) - require.EqualValues(t, "foo", fc.Name) - require.EqualValues(t, erc20Address, fc.Asset) - cctx, found := k.GetCrossChainTx(ctx, cctxIndex) - require.True(t, found) - require.EqualValues(t, fmt.Sprintf("%s:%s", constant.CmdWhitelistERC20, erc20Address), cctx.RelayedMessage) - - // check gas limit is set - gasLimit, err := zk.FungibleKeeper.QueryGasLimit(ctx, ethcommon.HexToAddress(zrc20)) - require.NoError(t, err) - require.Equal(t, uint64(100000), gasLimit.Uint64()) - - msgNew := types.MsgWhitelistERC20{ + keepertest.MockCheckAuthorization(&authorityMock.Mock, &msg, authoritytypes.ErrUnauthorized) + _, err := msgServer.WhitelistERC20(ctx, &msg) + require.ErrorIs(t, err, authoritytypes.ErrUnauthorized) + }) + + t.Run("should fail if invalid erc20 address", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseAuthorityMock: true, + }) + + msgServer := crosschainkeeper.NewMsgServerImpl(*k) + k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) + + admin := sample.AccAddress() + authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) + + msg := types.MsgWhitelistERC20{ Creator: admin, - Erc20Address: sample.EthAddress().Hex(), - ChainId: chainID, - Name: "bar", - Symbol: "BAR", + Erc20Address: "invalid", + ChainId: getValidEthChainID(), + Name: "foo", + Symbol: "FOO", Decimals: 18, GasLimit: 100000, } - keepertest.MockCheckAuthorization(&authorityMock.Mock, &msgNew, nil) + keepertest.MockCheckAuthorization(&authorityMock.Mock, &msg, nil) - // Ensure that whitelist a new erc20 create a cctx with a different index - res, err = msgServer.WhitelistERC20(ctx, &msgNew) - require.NoError(t, err) - require.NotNil(t, res) - require.NotEqual(t, cctxIndex, res.CctxIndex) + _, err := msgServer.WhitelistERC20(ctx, &msg) + require.ErrorIs(t, err, sdkerrors.ErrInvalidAddress) }) - t.Run("should fail if not authorized", func(t *testing.T) { - k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + t.Run("should fail if invalid spl address", func(t *testing.T) { + k, ctx, _, zk := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ UseAuthorityMock: true, }) @@ -102,22 +177,26 @@ func TestKeeper_WhitelistERC20(t *testing.T) { admin := sample.AccAddress() authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) + chainID := getValidSolanaChainID() + setSupportedChain(ctx, zk, chainID) + msg := types.MsgWhitelistERC20{ Creator: admin, - Erc20Address: sample.EthAddress().Hex(), - ChainId: getValidEthChainID(), + Erc20Address: "invalid", + ChainId: chainID, Name: "foo", Symbol: "FOO", Decimals: 18, GasLimit: 100000, } - keepertest.MockCheckAuthorization(&authorityMock.Mock, &msg, authoritytypes.ErrUnauthorized) + keepertest.MockCheckAuthorization(&authorityMock.Mock, &msg, nil) + _, err := msgServer.WhitelistERC20(ctx, &msg) - require.ErrorIs(t, err, authoritytypes.ErrUnauthorized) + require.ErrorIs(t, err, sdkerrors.ErrInvalidAddress) }) - t.Run("should fail if invalid erc20 address", func(t *testing.T) { - k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + t.Run("should fail if whitelisting not supported for chain", func(t *testing.T) { + k, ctx, _, zk := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ UseAuthorityMock: true, }) @@ -127,10 +206,13 @@ func TestKeeper_WhitelistERC20(t *testing.T) { admin := sample.AccAddress() authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) + chainID := getValidBtcChainID() + setSupportedChain(ctx, zk, chainID) + msg := types.MsgWhitelistERC20{ Creator: admin, Erc20Address: "invalid", - ChainId: getValidEthChainID(), + ChainId: chainID, Name: "foo", Symbol: "FOO", Decimals: 18, @@ -139,7 +221,7 @@ func TestKeeper_WhitelistERC20(t *testing.T) { keepertest.MockCheckAuthorization(&authorityMock.Mock, &msg, nil) _, err := msgServer.WhitelistERC20(ctx, &msg) - require.ErrorIs(t, err, sdkerrors.ErrInvalidAddress) + require.ErrorIs(t, err, sdkerrors.ErrInvalidChainID) }) t.Run("should fail if foreign coin already exists for the asset", func(t *testing.T) { diff --git a/x/crosschain/types/tx.pb.go b/x/crosschain/types/tx.pb.go index ffe5c8cec7..52facc862d 100644 --- a/x/crosschain/types/tx.pb.go +++ b/x/crosschain/types/tx.pb.go @@ -337,6 +337,7 @@ func (m *MsgAddInboundTrackerResponse) XXX_DiscardUnknown() { var xxx_messageInfo_MsgAddInboundTrackerResponse proto.InternalMessageInfo +// TODO: https://github.com/zeta-chain/node/issues/3083 type MsgWhitelistERC20 struct { Creator string `protobuf:"bytes,1,opt,name=creator,proto3" json:"creator,omitempty"` Erc20Address string `protobuf:"bytes,2,opt,name=erc20_address,json=erc20Address,proto3" json:"erc20_address,omitempty"` diff --git a/zetaclient/chains/solana/signer/signer.go b/zetaclient/chains/solana/signer/signer.go index a86b7eb659..2c402550f0 100644 --- a/zetaclient/chains/solana/signer/signer.go +++ b/zetaclient/chains/solana/signer/signer.go @@ -2,12 +2,14 @@ package signer import ( "context" + "fmt" "strings" "cosmossdk.io/errors" ethcommon "github.com/ethereum/go-ethereum/common" "github.com/gagliardetto/solana-go" "github.com/gagliardetto/solana-go/rpc" + "github.com/rs/zerolog" "github.com/zeta-chain/node/pkg/chains" "github.com/zeta-chain/node/pkg/coin" @@ -123,108 +125,40 @@ func (signer *Signer) TryProcessOutbound( nonce := params.TssNonce coinType := cctx.InboundParams.CoinType + // skip relaying the transaction if this signer hasn't set the relayer key + if !signer.HasRelayerKey() { + return + } + var tx *solana.Transaction switch coinType { case coin.CoinType_Cmd: - relayedMsg := strings.Split(cctx.RelayedMessage, ":") - if len(relayedMsg) != 2 { - return - } - - pk, err := solana.PublicKeyFromBase58(relayedMsg[1]) + whitelistTx, err := signer.prepareWhitelistTx(ctx, cctx, height) if err != nil { - signer.Logger(). - Std.Error(). - Err(err). - Msgf("TryProcessOutbound: error decoding spl from relayed msg") - return - } - - seed := [][]byte{[]byte("whitelist"), pk.Bytes()} - whitelistEntryPDA, _, err := solana.FindProgramAddress(seed, signer.gatewayID) - if err != nil { - signer.Logger(). - Std.Error(). - Err(err). - Msgf("TryProcessOutbound: error calculating whitelistEntry pda") - return - } - - // sign gateway whitelist message by TSS - msg, err := signer.SignMsgWhitelist(ctx, params, height, pk, whitelistEntryPDA) - if err != nil { - logger.Error(). - Err(err). - Msgf("TryProcessOutbound: SignMsgWhitelist error for chain %d nonce %d", chainID, nonce) - return - } - - // skip relaying the transaction if this signer hasn't set the relayer key - if !signer.HasRelayerKey() { + logger.Error().Err(err).Msgf("TryProcessOutbound: Fail to sign whitelist outbound") return } - // set relayer balance metrics - signer.SetRelayerBalanceMetrics(ctx) - - // sign the whitelist transaction by relayer key - tx, err = signer.SignWhitelistTx(ctx, msg) - if err != nil { - logger.Error(). - Err(err). - Msgf("TryProcessOutbound: SignWhitelistTx error for chain %d nonce %d", chainID, nonce) - return - } + tx = whitelistTx case coin.CoinType_Gas: - // compliance check - cancelTx := compliance.IsCctxRestricted(cctx) - if cancelTx { - compliance.PrintComplianceLog( - logger, - signer.Logger().Compliance, - true, - chainID, - cctx.Index, - cctx.InboundParams.Sender, - params.Receiver, - "SOL", - ) - } - - // sign gateway withdraw message by TSS - msg, err := signer.SignMsgWithdraw(ctx, params, height, cancelTx) + withdrawTx, err := signer.prepareWithdrawTx(ctx, cctx, height, logger) if err != nil { - logger.Error(). - Err(err). - Msgf("TryProcessOutbound: SignMsgWithdraw error for chain %d nonce %d", chainID, nonce) - return - } - - // skip relaying the transaction if this signer hasn't set the relayer key - if !signer.HasRelayerKey() { - return - } - - // set relayer balance metrics - signer.SetRelayerBalanceMetrics(ctx) - - // sign the withdraw transaction by relayer key - tx, err = signer.SignWithdrawTx(ctx, *msg) - if err != nil { - logger.Error(). - Err(err). - Msgf("TryProcessOutbound: SignGasWithdraw error for chain %d nonce %d", chainID, nonce) + logger.Error().Err(err).Msgf("TryProcessOutbound: Fail to sign withdraw outbound") return } + tx = withdrawTx default: logger.Error(). Msgf("TryProcessOutbound: can only send SOL to the Solana network for chain %d nonce %d", chainID, nonce) return } + // set relayer balance metrics + signer.SetRelayerBalanceMetrics(ctx) + // broadcast the signed tx to the Solana network with preflight check txSig, err := signer.client.SendTransactionWithOpts( ctx, @@ -248,6 +182,80 @@ func (signer *Signer) TryProcessOutbound( signer.reportToOutboundTracker(ctx, zetacoreClient, chainID, nonce, txSig, logger) } +func (signer *Signer) prepareWithdrawTx( + ctx context.Context, + cctx *types.CrossChainTx, + height uint64, + logger zerolog.Logger, +) (*solana.Transaction, error) { + params := cctx.GetCurrentOutboundParam() + // compliance check + cancelTx := compliance.IsCctxRestricted(cctx) + if cancelTx { + compliance.PrintComplianceLog( + logger, + signer.Logger().Compliance, + true, + signer.Chain().ChainId, + cctx.Index, + cctx.InboundParams.Sender, + params.Receiver, + "SOL", + ) + } + + // sign gateway withdraw message by TSS + msg, err := signer.SignMsgWithdraw(ctx, params, height, cancelTx) + if err != nil { + return nil, err + } + + // sign the withdraw transaction by relayer key + tx, err := signer.SignWithdrawTx(ctx, *msg) + if err != nil { + return nil, err + } + + return tx, nil +} + +func (signer *Signer) prepareWhitelistTx( + ctx context.Context, + cctx *types.CrossChainTx, + height uint64, +) (*solana.Transaction, error) { + params := cctx.GetCurrentOutboundParam() + relayedMsg := strings.Split(cctx.RelayedMessage, ":") + if len(relayedMsg) != 2 { + return nil, fmt.Errorf("TryProcessOutbound: invalid relayed msg") + } + + pk, err := solana.PublicKeyFromBase58(relayedMsg[1]) + if err != nil { + return nil, err + } + + seed := [][]byte{[]byte("whitelist"), pk.Bytes()} + whitelistEntryPDA, _, err := solana.FindProgramAddress(seed, signer.gatewayID) + if err != nil { + return nil, err + } + + // sign gateway whitelist message by TSS + msg, err := signer.SignMsgWhitelist(ctx, params, height, pk, whitelistEntryPDA) + if err != nil { + return nil, err + } + + // sign the whitelist transaction by relayer key + tx, err := signer.SignWhitelistTx(ctx, msg) + if err != nil { + return nil, err + } + + return tx, nil +} + // SetGatewayAddress sets the gateway address func (signer *Signer) SetGatewayAddress(address string) { // parse gateway ID and PDA From 5ddfc2a76a1655647f53b4c3a6fefd8330aeec0a Mon Sep 17 00:00:00 2001 From: skosito Date: Mon, 4 Nov 2024 02:42:12 +0100 Subject: [PATCH 18/20] fix after merge --- e2e/e2etests/test_solana_whitelist_spl.go | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/e2e/e2etests/test_solana_whitelist_spl.go b/e2e/e2etests/test_solana_whitelist_spl.go index 7ffe1bcb12..259657f72b 100644 --- a/e2e/e2etests/test_solana_whitelist_spl.go +++ b/e2e/e2etests/test_solana_whitelist_spl.go @@ -43,14 +43,12 @@ func TestSolanaWhitelistSPL(r *runner.E2ERunner, _ []string) { )) require.NoError(r, err) - // retrieve zrc20 and cctx from event - whitelistCCTXIndex, err := txserver.FetchAttributeFromTxResponse(res, "whitelist_cctx_index") - require.NoError(r, err) - - zrc20Addr, err := txserver.FetchAttributeFromTxResponse(res, "zrc20_address") - require.NoError(r, err) + event, ok := txserver.EventOfType[*crosschaintypes.EventERC20Whitelist](res.Events) + require.True(r, ok, "no EventERC20Whitelist in %s", res.TxHash) + erc20zrc20Addr := event.Zrc20Address + whitelistCCTXIndex := event.WhitelistCctxIndex - err = r.ZetaTxServer.InitializeLiquidityCap(zrc20Addr) + err = r.ZetaTxServer.InitializeLiquidityCaps(erc20zrc20Addr) require.NoError(r, err) // ensure CCTX created From b4ab0cdab2bc5861a0a5152c0d90298867a98e57 Mon Sep 17 00:00:00 2001 From: skosito Date: Mon, 4 Nov 2024 13:50:03 +0100 Subject: [PATCH 19/20] move back to tests solana and add todo --- cmd/zetae2e/local/local.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cmd/zetae2e/local/local.go b/cmd/zetae2e/local/local.go index da43aa50bb..e1d579cb0a 100644 --- a/cmd/zetae2e/local/local.go +++ b/cmd/zetae2e/local/local.go @@ -376,7 +376,6 @@ func localE2ETest(cmd *cobra.Command, _ []string) { e2etests.TestCriticalAdminTransactionsName, e2etests.TestPauseERC20CustodyName, e2etests.TestMigrateERC20CustodyFundsName, - e2etests.TestSolanaWhitelistSPLName, // Test the rate limiter functionalities // this test is currently incomplete and takes 10m to run @@ -410,6 +409,9 @@ func localE2ETest(cmd *cobra.Command, _ []string) { e2etests.TestSolanaDepositAndCallRefundName, e2etests.TestSolanaDepositRestrictedName, e2etests.TestSolanaWithdrawRestrictedName, + // TODO move under admin tests + // https://github.com/zeta-chain/node/issues/3085 + e2etests.TestSolanaWhitelistSPLName, } eg.Go(solanaTestRoutine(conf, deployerRunner, verbose, solanaTests...)) } From 051b30f70e3cc22b43537c90caf16a5c6fbf2bf8 Mon Sep 17 00:00:00 2001 From: skosito Date: Mon, 4 Nov 2024 16:28:27 +0100 Subject: [PATCH 20/20] PR comments --- e2e/runner/setup_solana.go | 2 +- e2e/runner/solana.go | 2 +- pkg/chains/chain.go | 4 +- pkg/contracts/solana/gateway.go | 43 ++++------- pkg/contracts/solana/gateway_message.go | 6 +- pkg/contracts/solana/gateway_message_test.go | 18 +++++ pkg/contracts/solana/instruction.go | 4 +- .../keeper/msg_server_whitelist_erc20.go | 2 +- zetaclient/chains/solana/observer/inbound.go | 2 +- .../chains/solana/observer/outbound_test.go | 63 +++++++++++++++ zetaclient/chains/solana/signer/signer.go | 16 ++-- zetaclient/chains/solana/signer/whitelist.go | 10 +-- zetaclient/chains/solana/signer/withdraw.go | 10 +-- ...vd6G9VbZZQPsEyn6RiTH4YBtqJ89omqfbbNNY.json | 76 +++++++++++++++++++ 14 files changed, 201 insertions(+), 57 deletions(-) create mode 100644 zetaclient/testdata/solana/chain_901_outbound_tx_result_phM9bESbiqojmpkkUxgjed8EABkxvPGNau9q31B8Yk1sXUtsxJvd6G9VbZZQPsEyn6RiTH4YBtqJ89omqfbbNNY.json diff --git a/e2e/runner/setup_solana.go b/e2e/runner/setup_solana.go index 0903f5476b..73a571b2be 100644 --- a/e2e/runner/setup_solana.go +++ b/e2e/runner/setup_solana.go @@ -53,7 +53,7 @@ func (r *E2ERunner) SetupSolana(deployerPrivateKey string) { inst.AccountValues = accountSlice inst.DataBytes, err = borsh.Serialize(solanacontracts.InitializeParams{ - Discriminator: solanacontracts.DiscriminatorInitialize(), + Discriminator: solanacontracts.DiscriminatorInitialize, TssAddress: r.TSSAddress, // #nosec G115 chain id always positive ChainID: uint64(chains.SolanaLocalnet.ChainId), diff --git a/e2e/runner/solana.go b/e2e/runner/solana.go index dede911181..24ea3c3b2f 100644 --- a/e2e/runner/solana.go +++ b/e2e/runner/solana.go @@ -51,7 +51,7 @@ func (r *E2ERunner) CreateDepositInstruction( var err error inst.DataBytes, err = borsh.Serialize(solanacontract.DepositInstructionParams{ - Discriminator: solanacontract.DiscriminatorDeposit(), + Discriminator: solanacontract.DiscriminatorDeposit, Amount: amount, Memo: append(receiver.Bytes(), data...), }) diff --git a/pkg/chains/chain.go b/pkg/chains/chain.go index 8d1b3c25c3..6731a70959 100644 --- a/pkg/chains/chain.go +++ b/pkg/chains/chain.go @@ -96,8 +96,8 @@ func (chain Chain) IsEVMChain() bool { return chain.Vm == Vm_evm } -func (chain Chain) IsSVMChain() bool { - return chain.Vm == Vm_svm +func (chain Chain) IsSolanaChain() bool { + return chain.Consensus == Consensus_solana_consensus } func (chain Chain) IsBitcoinChain() bool { diff --git a/pkg/contracts/solana/gateway.go b/pkg/contracts/solana/gateway.go index bf5ef91e15..a3adcf5eae 100644 --- a/pkg/contracts/solana/gateway.go +++ b/pkg/contracts/solana/gateway.go @@ -19,35 +19,20 @@ const ( AccountsNumDeposit = 3 ) -// DiscriminatorInitialize returns the discriminator for Solana gateway 'initialize' instruction -func DiscriminatorInitialize() [8]byte { - return idlgateway.IDLGateway.GetDiscriminator("initialize") -} - -// DiscriminatorDeposit returns the discriminator for Solana gateway 'deposit' instruction -func DiscriminatorDeposit() [8]byte { - return idlgateway.IDLGateway.GetDiscriminator("deposit") -} - -// DiscriminatorDepositSPL returns the discriminator for Solana gateway 'deposit_spl_token' instruction -func DiscriminatorDepositSPL() [8]byte { - return idlgateway.IDLGateway.GetDiscriminator("deposit_spl_token") -} - -// DiscriminatorWithdraw returns the discriminator for Solana gateway 'withdraw' instruction -func DiscriminatorWithdraw() [8]byte { - return idlgateway.IDLGateway.GetDiscriminator("withdraw") -} - -// DiscriminatorWithdrawSPL returns the discriminator for Solana gateway 'withdraw_spl_token' instruction -func DiscriminatorWithdrawSPL() [8]byte { - return idlgateway.IDLGateway.GetDiscriminator("withdraw_spl_token") -} - -// DiscriminatorWhitelist returns the discriminator for Solana gateway 'whitelist_spl_mint' instruction -func DiscriminatorWhitelistSplMint() [8]byte { - return idlgateway.IDLGateway.GetDiscriminator("whitelist_spl_mint") -} +var ( + // DiscriminatorInitialize returns the discriminator for Solana gateway 'initialize' instruction + DiscriminatorInitialize = idlgateway.IDLGateway.GetDiscriminator("initialize") + // DiscriminatorDeposit returns the discriminator for Solana gateway 'deposit' instruction + DiscriminatorDeposit = idlgateway.IDLGateway.GetDiscriminator("deposit") + // DiscriminatorDepositSPL returns the discriminator for Solana gateway 'deposit_spl_token' instruction + DiscriminatorDepositSPL = idlgateway.IDLGateway.GetDiscriminator("deposit_spl_token") + // DiscriminatorWithdraw returns the discriminator for Solana gateway 'withdraw' instruction + DiscriminatorWithdraw = idlgateway.IDLGateway.GetDiscriminator("withdraw") + // DiscriminatorWithdrawSPL returns the discriminator for Solana gateway 'withdraw_spl_token' instruction + DiscriminatorWithdrawSPL = idlgateway.IDLGateway.GetDiscriminator("withdraw_spl_token") + // DiscriminatorWhitelist returns the discriminator for Solana gateway 'whitelist_spl_mint' instruction + DiscriminatorWhitelistSplMint = idlgateway.IDLGateway.GetDiscriminator("whitelist_spl_mint") +) // ParseGatewayAddressAndPda parses the gateway id and program derived address from the given string func ParseGatewayIDAndPda(address string) (solana.PublicKey, solana.PublicKey, error) { diff --git a/pkg/contracts/solana/gateway_message.go b/pkg/contracts/solana/gateway_message.go index 65c67f3ee0..1c8abaca23 100644 --- a/pkg/contracts/solana/gateway_message.go +++ b/pkg/contracts/solana/gateway_message.go @@ -110,9 +110,11 @@ func (msg *MsgWithdraw) Signer() (common.Address, error) { // MsgWhitelist is the message for the Solana gateway whitelist_spl_mint instruction type MsgWhitelist struct { - // whitelistCandidate is the whitelist candidate + // whitelistCandidate is the SPL token to be whitelisted in gateway program whitelistCandidate solana.PublicKey - whitelistEntry solana.PublicKey + + // whitelistEntry is the entry in gateway program representing whitelisted SPL token + whitelistEntry solana.PublicKey // chainID is the chain ID of Solana chain chainID uint64 diff --git a/pkg/contracts/solana/gateway_message_test.go b/pkg/contracts/solana/gateway_message_test.go index a7dd1e8a12..68af93e859 100644 --- a/pkg/contracts/solana/gateway_message_test.go +++ b/pkg/contracts/solana/gateway_message_test.go @@ -29,3 +29,21 @@ func Test_MsgWithdrawHash(t *testing.T) { require.True(t, bytes.Equal(hash[:], wantHashBytes)) }) } + +func Test_MsgWhitelistHash(t *testing.T) { + t.Run("should pass for archived inbound, receipt and cctx", func(t *testing.T) { + // #nosec G115 always positive + chainID := uint64(chains.SolanaLocalnet.ChainId) + nonce := uint64(0) + whitelistCandidate := solana.MustPublicKeyFromBase58("37yGiHAnLvWZUNVwu9esp74YQFqxU1qHCbABkDvRddUQ") + whitelistEntry := solana.MustPublicKeyFromBase58("2kJndCL9NBR36ySiQ4bmArs4YgWQu67LmCDfLzk5Gb7s") + + wantHash := "cde8fa3ab24b50320db1c47f30492e789177d28e76208176f0a52b8ed54ce2dd" + wantHashBytes, err := hex.DecodeString(wantHash) + require.NoError(t, err) + + // create new withdraw message + hash := contracts.NewMsgWhitelist(whitelistCandidate, whitelistEntry, chainID, nonce).Hash() + require.True(t, bytes.Equal(hash[:], wantHashBytes)) + }) +} diff --git a/pkg/contracts/solana/instruction.go b/pkg/contracts/solana/instruction.go index c0956263ae..df5db0416b 100644 --- a/pkg/contracts/solana/instruction.go +++ b/pkg/contracts/solana/instruction.go @@ -99,7 +99,7 @@ func ParseInstructionWithdraw(instruction solana.CompiledInstruction) (*Withdraw } // check the discriminator to ensure it's a 'withdraw' instruction - if inst.Discriminator != DiscriminatorWithdraw() { + if inst.Discriminator != DiscriminatorWithdraw { return nil, fmt.Errorf("not a withdraw instruction: %v", inst.Discriminator) } @@ -167,7 +167,7 @@ func ParseInstructionWhitelist(instruction solana.CompiledInstruction) (*Whiteli } // check the discriminator to ensure it's a 'whitelist_spl_mint' instruction - if inst.Discriminator != DiscriminatorWhitelistSplMint() { + if inst.Discriminator != DiscriminatorWhitelistSplMint { return nil, fmt.Errorf("not a whitelist_spl_mint instruction: %v", inst.Discriminator) } diff --git a/x/crosschain/keeper/msg_server_whitelist_erc20.go b/x/crosschain/keeper/msg_server_whitelist_erc20.go index 979ecc2850..197310e16c 100644 --- a/x/crosschain/keeper/msg_server_whitelist_erc20.go +++ b/x/crosschain/keeper/msg_server_whitelist_erc20.go @@ -48,7 +48,7 @@ func (k msgServer) WhitelistERC20( ) } - case chain.IsSVMChain(): + case chain.IsSolanaChain(): _, err := solana.PublicKeyFromBase58(msg.Erc20Address) if err != nil { return nil, errorsmod.Wrapf( diff --git a/zetaclient/chains/solana/observer/inbound.go b/zetaclient/chains/solana/observer/inbound.go index 1441150ada..4c93d95470 100644 --- a/zetaclient/chains/solana/observer/inbound.go +++ b/zetaclient/chains/solana/observer/inbound.go @@ -281,7 +281,7 @@ func (ob *Observer) ParseInboundAsDeposit( } // check if the instruction is a deposit or not - if inst.Discriminator != solanacontracts.DiscriminatorDeposit() { + if inst.Discriminator != solanacontracts.DiscriminatorDeposit { return nil, nil } diff --git a/zetaclient/chains/solana/observer/outbound_test.go b/zetaclient/chains/solana/observer/outbound_test.go index 5cb2b80a5c..73af8da573 100644 --- a/zetaclient/chains/solana/observer/outbound_test.go +++ b/zetaclient/chains/solana/observer/outbound_test.go @@ -35,6 +35,9 @@ const ( // tssAddressTest is the TSS address for testing tssAddressTest = "0x05C7dBdd1954D59c9afaB848dA7d8DD3F35e69Cd" + + // whitelistTxTest is local devnet tx result for testing + whitelistTxTest = "phM9bESbiqojmpkkUxgjed8EABkxvPGNau9q31B8Yk1sXUtsxJvd6G9VbZZQPsEyn6RiTH4YBtqJ89omqfbbNNY" ) // createTestObserver creates a test observer for testing @@ -294,3 +297,63 @@ func Test_ParseInstructionWithdraw(t *testing.T) { require.Nil(t, inst) }) } + +func Test_ParseInstructionWhitelist(t *testing.T) { + // the test chain and transaction hash + chain := chains.SolanaDevnet + txHash := whitelistTxTest + txAmount := uint64(0) + + t.Run("should parse instruction whitelist", func(t *testing.T) { + // tss address used in local devnet + tssAddress := "0x7E8c7bAcd3c6220DDC35A4EA1141BE14F2e1dFEB" + // load and unmarshal archived transaction + txResult := testutils.LoadSolanaOutboundTxResult(t, TestDataDir, chain.ChainId, txHash) + tx, err := txResult.Transaction.GetTransaction() + require.NoError(t, err) + + instruction := tx.Message.Instructions[0] + inst, err := contracts.ParseInstructionWhitelist(instruction) + require.NoError(t, err) + + // check sender, nonce and amount + sender, err := inst.Signer() + require.NoError(t, err) + require.Equal(t, tssAddress, sender.String()) + require.EqualValues(t, inst.GatewayNonce(), 3) + require.EqualValues(t, inst.TokenAmount(), txAmount) + }) + + t.Run("should return error on invalid instruction data", func(t *testing.T) { + // load and unmarshal archived transaction + txResult := testutils.LoadSolanaOutboundTxResult(t, TestDataDir, chain.ChainId, txHash) + txFake, err := txResult.Transaction.GetTransaction() + require.NoError(t, err) + + // set invalid instruction data + instruction := txFake.Message.Instructions[0] + instruction.Data = []byte("invalid instruction data") + + inst, err := contracts.ParseInstructionWhitelist(instruction) + require.ErrorContains(t, err, "error deserializing instruction") + require.Nil(t, inst) + }) + + t.Run("should return error on discriminator mismatch", func(t *testing.T) { + // load and unmarshal archived transaction + txResult := testutils.LoadSolanaOutboundTxResult(t, TestDataDir, chain.ChainId, txHash) + txFake, err := txResult.Transaction.GetTransaction() + require.NoError(t, err) + + // overwrite discriminator (first 8 bytes) + instruction := txFake.Message.Instructions[0] + fakeDiscriminator := "b712469c946da12100980d0000000000" + fakeDiscriminatorBytes, err := hex.DecodeString(fakeDiscriminator) + require.NoError(t, err) + copy(instruction.Data, fakeDiscriminatorBytes) + + inst, err := contracts.ParseInstructionWhitelist(instruction) + require.ErrorContains(t, err, "not a whitelist_spl_mint instruction") + require.Nil(t, inst) + }) +} diff --git a/zetaclient/chains/solana/signer/signer.go b/zetaclient/chains/solana/signer/signer.go index 2c402550f0..8e180f8c7f 100644 --- a/zetaclient/chains/solana/signer/signer.go +++ b/zetaclient/chains/solana/signer/signer.go @@ -127,6 +127,7 @@ func (signer *Signer) TryProcessOutbound( // skip relaying the transaction if this signer hasn't set the relayer key if !signer.HasRelayerKey() { + logger.Warn().Msgf("TryProcessOutbound: no relayer key configured") return } @@ -152,7 +153,7 @@ func (signer *Signer) TryProcessOutbound( tx = withdrawTx default: logger.Error(). - Msgf("TryProcessOutbound: can only send SOL to the Solana network for chain %d nonce %d", chainID, nonce) + Msgf("TryProcessOutbound: can only send SOL to the Solana network") return } @@ -171,10 +172,9 @@ func (signer *Signer) TryProcessOutbound( rpc.TransactionOpts{PreflightCommitment: rpc.CommitmentProcessed}, ) if err != nil { - signer.Logger(). - Std.Warn(). + logger.Error(). Err(err). - Msgf("TryProcessOutbound: broadcast error for chain %d nonce %d", chainID, nonce) + Msgf("TryProcessOutbound: broadcast error") return } @@ -205,13 +205,13 @@ func (signer *Signer) prepareWithdrawTx( } // sign gateway withdraw message by TSS - msg, err := signer.SignMsgWithdraw(ctx, params, height, cancelTx) + msg, err := signer.createAndSignMsgWithdraw(ctx, params, height, cancelTx) if err != nil { return nil, err } // sign the withdraw transaction by relayer key - tx, err := signer.SignWithdrawTx(ctx, *msg) + tx, err := signer.signWithdrawTx(ctx, *msg) if err != nil { return nil, err } @@ -242,13 +242,13 @@ func (signer *Signer) prepareWhitelistTx( } // sign gateway whitelist message by TSS - msg, err := signer.SignMsgWhitelist(ctx, params, height, pk, whitelistEntryPDA) + msg, err := signer.createAndSignMsgWhitelist(ctx, params, height, pk, whitelistEntryPDA) if err != nil { return nil, err } // sign the whitelist transaction by relayer key - tx, err := signer.SignWhitelistTx(ctx, msg) + tx, err := signer.signWhitelistTx(ctx, msg) if err != nil { return nil, err } diff --git a/zetaclient/chains/solana/signer/whitelist.go b/zetaclient/chains/solana/signer/whitelist.go index dd917b36d2..73ee769039 100644 --- a/zetaclient/chains/solana/signer/whitelist.go +++ b/zetaclient/chains/solana/signer/whitelist.go @@ -12,8 +12,8 @@ import ( "github.com/zeta-chain/node/x/crosschain/types" ) -// SignMsgWhitelist signs a whitelist message (for gateway whitelist_spl_mint instruction) with TSS. -func (signer *Signer) SignMsgWhitelist( +// createAndSignMsgWhitelist creates and signs a whitelist message (for gateway whitelist_spl_mint instruction) with TSS. +func (signer *Signer) createAndSignMsgWhitelist( ctx context.Context, params *types.OutboundParams, height uint64, @@ -41,13 +41,13 @@ func (signer *Signer) SignMsgWhitelist( return msg.SetSignature(signature), nil } -// SignWhitelistTx wraps the whitelist 'msg' into a Solana transaction and signs it with the relayer key. -func (signer *Signer) SignWhitelistTx(ctx context.Context, msg *contracts.MsgWhitelist) (*solana.Transaction, error) { +// signWhitelistTx wraps the whitelist 'msg' into a Solana transaction and signs it with the relayer key. +func (signer *Signer) signWhitelistTx(ctx context.Context, msg *contracts.MsgWhitelist) (*solana.Transaction, error) { // create whitelist_spl_mint instruction with program call data var err error var inst solana.GenericInstruction inst.DataBytes, err = borsh.Serialize(contracts.WhitelistInstructionParams{ - Discriminator: contracts.DiscriminatorWhitelistSplMint(), + Discriminator: contracts.DiscriminatorWhitelistSplMint, Signature: msg.SigRS(), RecoveryID: msg.SigV(), MessageHash: msg.Hash(), diff --git a/zetaclient/chains/solana/signer/withdraw.go b/zetaclient/chains/solana/signer/withdraw.go index 58411b43bb..51f4cceeea 100644 --- a/zetaclient/chains/solana/signer/withdraw.go +++ b/zetaclient/chains/solana/signer/withdraw.go @@ -13,8 +13,8 @@ import ( "github.com/zeta-chain/node/x/crosschain/types" ) -// SignMsgWithdraw signs a withdraw message (for gateway withdraw/withdraw_spl instruction) with TSS. -func (signer *Signer) SignMsgWithdraw( +// createAndSignMsgWithdraw creates and signs a withdraw message (for gateway withdraw/withdraw_spl instruction) with TSS. +func (signer *Signer) createAndSignMsgWithdraw( ctx context.Context, params *types.OutboundParams, height uint64, @@ -53,13 +53,13 @@ func (signer *Signer) SignMsgWithdraw( return msg.SetSignature(signature), nil } -// SignWithdrawTx wraps the withdraw 'msg' into a Solana transaction and signs it with the relayer key. -func (signer *Signer) SignWithdrawTx(ctx context.Context, msg contracts.MsgWithdraw) (*solana.Transaction, error) { +// signWithdrawTx wraps the withdraw 'msg' into a Solana transaction and signs it with the relayer key. +func (signer *Signer) signWithdrawTx(ctx context.Context, msg contracts.MsgWithdraw) (*solana.Transaction, error) { // create withdraw instruction with program call data var err error var inst solana.GenericInstruction inst.DataBytes, err = borsh.Serialize(contracts.WithdrawInstructionParams{ - Discriminator: contracts.DiscriminatorWithdraw(), + Discriminator: contracts.DiscriminatorWithdraw, Amount: msg.Amount(), Signature: msg.SigRS(), RecoveryID: msg.SigV(), diff --git a/zetaclient/testdata/solana/chain_901_outbound_tx_result_phM9bESbiqojmpkkUxgjed8EABkxvPGNau9q31B8Yk1sXUtsxJvd6G9VbZZQPsEyn6RiTH4YBtqJ89omqfbbNNY.json b/zetaclient/testdata/solana/chain_901_outbound_tx_result_phM9bESbiqojmpkkUxgjed8EABkxvPGNau9q31B8Yk1sXUtsxJvd6G9VbZZQPsEyn6RiTH4YBtqJ89omqfbbNNY.json new file mode 100644 index 0000000000..c488facac3 --- /dev/null +++ b/zetaclient/testdata/solana/chain_901_outbound_tx_result_phM9bESbiqojmpkkUxgjed8EABkxvPGNau9q31B8Yk1sXUtsxJvd6G9VbZZQPsEyn6RiTH4YBtqJ89omqfbbNNY.json @@ -0,0 +1,76 @@ +{ + "slot": 1109, + "blockTime": 1730732052, + "transaction": { + "signatures": [ + "phM9bESbiqojmpkkUxgjed8EABkxvPGNau9q31B8Yk1sXUtsxJvd6G9VbZZQPsEyn6RiTH4YBtqJ89omqfbbNNY" + ], + "message": { + "accountKeys": [ + "2qBVcNBZCubcnSR3NyCnFjCfkCVUB3G7ECPoaW5rxVjx", + "3eXQYW8nC9142kJUHRgZ9RggJaMgpAEtnZPrwPT7CdxH", + "9dcAyYG4bawApZocwZSyJBi9Mynf5EuKAJfifXdfkqik", + "GNQPa92uBDem5ZFH16TkmFwN5EN8LAzkqeRrxsxZt4eD", + "11111111111111111111111111111111", + "94U5AHQMKkV5txNJ17QPXWoh474PheGou6cNP2FEuL1d" + ], + "header": { + "numRequiredSignatures": 1, + "numReadonlySignedAccounts": 0, + "numReadonlyUnsignedAccounts": 3 + }, + "recentBlockhash": "94KjHcf2zDN6VtbFnVU3vkEqJv4d5jWCzBTvg6PtPkdB", + "instructions": [ + { + "programIdIndex": 5, + "accounts": [ + 1, + 3, + 2, + 0, + 4 + ], + "data": "SDhLNtfumZy7dZ96HRWDWWC9NHtnc54NUDt3XAAY8msc42QtH8rF3nYfFcmFjX64KsoMSYNtkWQTv4iVU3Ly36a5ff3nEU5aPbgeBGAPsMbnEiX1bz51dHoyMJjpKxvWJbmCxEG6Z8tA1Tk4EcY39DTDRH" + } + ] + } + }, + "meta": { + "err": null, + "fee": 5000, + "preBalances": [ + 99999985000, + 14947680, + 1461600, + 1, + 1141440 + ], + "postBalances": [ + 99999033440, + 946560, + 14947680, + 1461600, + 1, + 1141440 + ], + "innerInstructions": [], + "preTokenBalances": [], + "postTokenBalances": [], + "logMessages": [ + "Program 94U5AHQMKkV5txNJ17QPXWoh474PheGou6cNP2FEuL1d invoke [1]", + "Program log: Instruction: WhitelistSplMint Program 11111111111111111111111111111111 invoke [2]", + "Program 11111111111111111111111111111111 success", + "Program log: recovered address [126, 140, 123, 172, 211, 198, 34, 13, 220, 53, 164, 234, 17, 65, 190, 20, 242, 225, 223, 235]", + "Program 94U5AHQMKkV5txNJ17QPXWoh474PheGou6cNP2FEuL1d consumed 46731 of 200000 compute units", + "Program 94U5AHQMKkV5txNJ17QPXWoh474PheGou6cNP2FEuL1d success" + ], + "status": { "Ok": null }, + "rewards": [], + "loadedAddresses": { + "readonly": [], + "writable": [] + }, + "computeUnitsConsumed": 274896977280 + }, + "version": 0 +} \ No newline at end of file