Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: extend solana unit tests #3158

Merged
merged 8 commits into from
Nov 15, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions e2e/runner/setup_solana.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,8 @@ func (r *E2ERunner) SetupSolana(deployerPrivateKey string) {
require.NoError(r, err)

// deploy test spl
tokenAccount := r.DeploySPL(&privkey, true)
r.SPLAddr = tokenAccount.PublicKey()
mintAccount := r.DeploySPL(&privkey, true)
r.SPLAddr = mintAccount.PublicKey()
}

func (r *E2ERunner) ensureSolanaChainParams() error {
Expand Down
36 changes: 18 additions & 18 deletions e2e/runner/solana.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,9 +163,9 @@ func (r *E2ERunner) CreateSignedTransaction(
func (r *E2ERunner) ResolveSolanaATA(
payer solana.PrivateKey,
owner solana.PublicKey,
tokenAccount solana.PublicKey,
mintAccount solana.PublicKey,
) solana.PublicKey {
pdaAta, _, err := solana.FindAssociatedTokenAddress(owner, tokenAccount)
pdaAta, _, err := solana.FindAssociatedTokenAddress(owner, mintAccount)
require.NoError(r, err)

info, _ := r.SolanaClient.GetAccountInfo(r.Ctx, pdaAta)
Expand All @@ -174,7 +174,7 @@ func (r *E2ERunner) ResolveSolanaATA(
return pdaAta
}
// doesn't exist, create it
ataInstruction := associatedtokenaccount.NewCreateInstruction(payer.PublicKey(), owner, tokenAccount).Build()
ataInstruction := associatedtokenaccount.NewCreateInstruction(payer.PublicKey(), owner, mintAccount).Build()
signedTx := r.CreateSignedTransaction(
[]solana.Instruction{ataInstruction},
payer,
Expand All @@ -190,27 +190,27 @@ func (r *E2ERunner) ResolveSolanaATA(
func (r *E2ERunner) SPLDepositAndCall(
privateKey *solana.PrivateKey,
amount uint64,
tokenAccount solana.PublicKey,
mintAccount solana.PublicKey,
receiver ethcommon.Address,
data []byte,
) solana.Signature {
// ata for pda
pda := r.ComputePdaAddress()
pdaAta := r.ResolveSolanaATA(*privateKey, pda, tokenAccount)
pdaAta := r.ResolveSolanaATA(*privateKey, pda, mintAccount)

// deployer ata
ata := r.ResolveSolanaATA(*privateKey, privateKey.PublicKey(), tokenAccount)
ata := r.ResolveSolanaATA(*privateKey, privateKey.PublicKey(), mintAccount)

// deposit spl
seed := [][]byte{[]byte("whitelist"), tokenAccount.Bytes()}
seed := [][]byte{[]byte("whitelist"), mintAccount.Bytes()}
whitelistEntryPDA, _, err := solana.FindProgramAddress(seed, r.GatewayProgram)
require.NoError(r, err)

depositSPLInstruction := r.CreateDepositSPLInstruction(
amount,
privateKey.PublicKey(),
whitelistEntryPDA,
tokenAccount,
mintAccount,
ata,
pdaAta,
receiver,
Expand All @@ -233,36 +233,36 @@ func (r *E2ERunner) DeploySPL(privateKey *solana.PrivateKey, whitelist bool) *so
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()
mintAccount := solana.NewWallet()
createAccountInstruction := system.NewCreateAccountInstruction(
lamport,
token.MINT_SIZE,
solana.TokenProgramID,
privateKey.PublicKey(),
tokenAccount.PublicKey(),
mintAccount.PublicKey(),
).Build()

initializeMintInstruction := token.NewInitializeMint2Instruction(
6,
privateKey.PublicKey(),
privateKey.PublicKey(),
tokenAccount.PublicKey(),
mintAccount.PublicKey(),
).Build()

signedTx := r.CreateSignedTransaction(
[]solana.Instruction{createAccountInstruction, initializeMintInstruction},
*privateKey,
[]solana.PrivateKey{tokenAccount.PrivateKey},
[]solana.PrivateKey{mintAccount.PrivateKey},
)

// broadcast the transaction and wait for finalization
_, out := r.BroadcastTxSync(signedTx)
r.Logger.Info("create spl logs: %v", out.Meta.LogMessages)

// minting some tokens to deployer for testing
ata := r.ResolveSolanaATA(*privateKey, privateKey.PublicKey(), tokenAccount.PublicKey())
ata := r.ResolveSolanaATA(*privateKey, privateKey.PublicKey(), mintAccount.PublicKey())

mintToInstruction := token.NewMintToInstruction(uint64(1_000_000_000), tokenAccount.PublicKey(), ata, privateKey.PublicKey(), []solana.PublicKey{}).
mintToInstruction := token.NewMintToInstruction(uint64(1_000_000_000), mintAccount.PublicKey(), ata, privateKey.PublicKey(), []solana.PublicKey{}).
Build()
signedTx = r.CreateSignedTransaction(
[]solana.Instruction{mintToInstruction},
Expand All @@ -276,7 +276,7 @@ func (r *E2ERunner) DeploySPL(privateKey *solana.PrivateKey, whitelist bool) *so

// optionally whitelist spl token in gateway
if whitelist {
seed := [][]byte{[]byte("whitelist"), tokenAccount.PublicKey().Bytes()}
seed := [][]byte{[]byte("whitelist"), mintAccount.PublicKey().Bytes()}
whitelistEntryPDA, _, err := solana.FindProgramAddress(seed, r.GatewayProgram)
require.NoError(r, err)

Expand All @@ -285,14 +285,14 @@ func (r *E2ERunner) DeploySPL(privateKey *solana.PrivateKey, whitelist bool) *so

// already whitelisted
if whitelistEntryInfo != nil {
return tokenAccount
return mintAccount
}

// create 'whitelist_spl_mint' instruction
instruction := r.CreateWhitelistSPLMintInstruction(
privateKey.PublicKey(),
whitelistEntryPDA,
tokenAccount.PublicKey(),
mintAccount.PublicKey(),
)
// create and sign the transaction
signedTx := r.CreateSignedTransaction([]solana.Instruction{instruction}, *privateKey, []solana.PrivateKey{})
Expand All @@ -306,7 +306,7 @@ func (r *E2ERunner) DeploySPL(privateKey *solana.PrivateKey, whitelist bool) *so
require.NotNil(r, whitelistEntryInfo)
}

return tokenAccount
return mintAccount
}

// BroadcastTxSync broadcasts a transaction and waits for it to be finalized
Expand Down
2 changes: 1 addition & 1 deletion pkg/contracts/solana/gateway.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ func ParseGatewayWithPDA(gatewayAddress string) (solana.PublicKey, solana.Public
return gatewayID, pda, err
}

// ParseRentPayerPda parses the rent payer program derived address from the given string
// ParseRentPayerPDA parses the rent payer program derived address from the given string
func RentPayerPDA(gateway solana.PublicKey) (solana.PublicKey, error) {
var rentPayerPda solana.PublicKey
seed := []byte(RentPayerPDASeed)
Expand Down
14 changes: 7 additions & 7 deletions pkg/contracts/solana/gateway_message.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,8 @@
// amount is the lamports amount for the withdraw_spl
amount uint64

// tokenAccount is the address for the spl token
tokenAccount solana.PublicKey
// mintAccount is the address for the spl token
mintAccount solana.PublicKey

// decimals of spl token
decimals uint8
Expand All @@ -139,15 +139,15 @@
func NewMsgWithdrawSPL(
chainID, nonce, amount uint64,
decimals uint8,
tokenAccount, to, toAta solana.PublicKey,
mintAccount, to, toAta solana.PublicKey,
) *MsgWithdrawSPL {
return &MsgWithdrawSPL{
chainID: chainID,
nonce: nonce,
amount: amount,
to: to,
recipientAta: toAta,
tokenAccount: tokenAccount,
mintAccount: mintAccount,
decimals: decimals,
}
}
Expand Down Expand Up @@ -176,8 +176,8 @@
return msg.recipientAta
}

func (msg *MsgWithdrawSPL) TokenAccount() solana.PublicKey {
return msg.tokenAccount
func (msg *MsgWithdrawSPL) MintAccount() solana.PublicKey {
return msg.mintAccount

Check warning on line 180 in pkg/contracts/solana/gateway_message.go

View check run for this annotation

Codecov / codecov/patch

pkg/contracts/solana/gateway_message.go#L179-L180

Added lines #L179 - L180 were not covered by tests
}

func (msg *MsgWithdrawSPL) Decimals() uint8 {
Expand All @@ -200,7 +200,7 @@
binary.BigEndian.PutUint64(buff, msg.amount)
message = append(message, buff...)

message = append(message, msg.tokenAccount.Bytes()...)
message = append(message, msg.mintAccount.Bytes()...)

message = append(message, msg.recipientAta.Bytes()...)

Expand Down
21 changes: 21 additions & 0 deletions pkg/contracts/solana/gateway_message_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,24 @@ func Test_MsgWhitelistHash(t *testing.T) {
require.True(t, bytes.Equal(hash[:], wantHashBytes))
})
}

func Test_MsgWithdrawSPLHash(t *testing.T) {
t.Run("should pass for archived inbound, receipt and cctx", func(t *testing.T) {
skosito marked this conversation as resolved.
Show resolved Hide resolved
// #nosec G115 always positive
skosito marked this conversation as resolved.
Show resolved Hide resolved
chainID := uint64(chains.SolanaLocalnet.ChainId)
nonce := uint64(0)
amount := uint64(1336000)
mintAccount := solana.MustPublicKeyFromBase58("AS48jKNQsDGkEdDvfwu1QpqjtqbCadrAq9nGXjFmdX3Z")
to := solana.MustPublicKeyFromBase58("37yGiHAnLvWZUNVwu9esp74YQFqxU1qHCbABkDvRddUQ")
toAta, _, err := solana.FindAssociatedTokenAddress(to, mintAccount)
require.NoError(t, err)

wantHash := "87fa5c0ed757c6e1ea9d8976537eaf7868bc1f1bbf55ab198a01645d664fe0ae"
wantHashBytes, err := hex.DecodeString(wantHash)
skosito marked this conversation as resolved.
Show resolved Hide resolved
require.NoError(t, err)

// create new withdraw message
hash := contracts.NewMsgWithdrawSPL(chainID, nonce, amount, 8, mintAccount, to, toAta).Hash()
require.True(t, bytes.Equal(hash[:], wantHashBytes))
skosito marked this conversation as resolved.
Show resolved Hide resolved
})
}
skosito marked this conversation as resolved.
Show resolved Hide resolved
43 changes: 31 additions & 12 deletions pkg/contracts/solana/inbound.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,34 +94,53 @@
}, nil
}

// GetSignerDeposit returns the signer address of the deposit instruction
// Note: solana-go is not able to parse the AccountMeta 'is_signer' ATM. This is a workaround.
// getSignerDeposit returns the signer address of the deposit instruction
func getSignerDeposit(tx *solana.Transaction, inst *solana.CompiledInstruction) (string, error) {
instructionAccounts, err := inst.ResolveInstructionAccounts(&tx.Message)
if err != nil {
return "", err
}

Check warning on line 102 in pkg/contracts/solana/inbound.go

View check run for this annotation

Codecov / codecov/patch

pkg/contracts/solana/inbound.go#L101-L102

Added lines #L101 - L102 were not covered by tests
skosito marked this conversation as resolved.
Show resolved Hide resolved

// there should be 3 accounts for a deposit instruction
if len(inst.Accounts) != accountsNumDeposit {
return "", fmt.Errorf("want %d accounts, got %d", accountsNumDeposit, len(inst.Accounts))
if len(instructionAccounts) != accountsNumDeposit {
return "", fmt.Errorf("want %d accounts, got %d", accountsNumDeposit, len(instructionAccounts))
}

// sender is the signer account
return tx.Message.AccountKeys[0].String(), nil
// the accounts are [signer, pda, system_program]
// check if first account is signer
if !instructionAccounts[0].IsSigner {
ws4charlie marked this conversation as resolved.
Show resolved Hide resolved
return "", fmt.Errorf("unexpected signer %s", instructionAccounts[0].PublicKey.String())
skosito marked this conversation as resolved.
Show resolved Hide resolved
}

return instructionAccounts[0].PublicKey.String(), nil
}

// getSignerAndSPLFromDepositSPLAccounts returns the signer and spl address of the deposit_spl instruction
func getSignerAndSPLFromDepositSPLAccounts(
tx *solana.Transaction,
inst *solana.CompiledInstruction,
) (string, string, error) {
instructionAccounts, err := inst.ResolveInstructionAccounts(&tx.Message)
if err != nil {
return "", "", err
}

Check warning on line 126 in pkg/contracts/solana/inbound.go

View check run for this annotation

Codecov / codecov/patch

pkg/contracts/solana/inbound.go#L125-L126

Added lines #L125 - L126 were not covered by tests
skosito marked this conversation as resolved.
Show resolved Hide resolved

// there should be 7 accounts for a deposit spl instruction
if len(inst.Accounts) != accountsNumberDepositSPL {
if len(instructionAccounts) != accountsNumberDepositSPL {
return "", "", fmt.Errorf(
"want %d accounts, got %d",
accountsNumberDepositSPL,
len(inst.Accounts),
len(instructionAccounts),
)
}

// the accounts are [signer, pda, whitelist_entry, mint_account, token_program, from, to]
signer := tx.Message.AccountKeys[0]
spl := tx.Message.AccountKeys[inst.Accounts[3]]
// check if first account is signer
if !instructionAccounts[0].IsSigner {
return "", "", fmt.Errorf("unexpected signer %s", instructionAccounts[0].PublicKey.String())
}

signer := instructionAccounts[0].PublicKey.String()
spl := instructionAccounts[3].PublicKey.String()

return signer.String(), spl.String(), nil
return signer, spl, nil
}
Loading
Loading