diff --git a/client/client_test.go b/client/client_test.go index 81efe79..5e5e97e 100644 --- a/client/client_test.go +++ b/client/client_test.go @@ -1108,17 +1108,19 @@ func TestClient_Kin4AccountManagement(t *testing.T) { priv, err := NewPrivateKey() require.NoError(t, err) - balance, err := env.client.GetBalance(context.Background(), priv.Public()) + tokenAcc, _ := generateTokenAccount(ed25519.PrivateKey(priv)) + + balance, err := env.client.GetBalance(context.Background(), PublicKey(tokenAcc)) assert.Equal(t, ErrAccountDoesNotExist, err) assert.Zero(t, balance) - err = env.client.CreateAccount(context.Background(), PrivateKey(priv)) + err = env.client.CreateAccount(context.Background(), priv) assert.NoError(t, err) - err = env.client.CreateAccount(context.Background(), PrivateKey(priv)) + err = env.client.CreateAccount(context.Background(), priv) assert.Equal(t, ErrAccountExists, err) - balance, err = env.client.GetBalance(context.Background(), priv.Public()) + balance, err = env.client.GetBalance(context.Background(), PublicKey(tokenAcc)) assert.NoError(t, err) assert.EqualValues(t, 10, balance) } @@ -1931,7 +1933,9 @@ func TestClient_CreateAccountMigration(t *testing.T) { env.v4Server.Mux.Lock() assert.Len(t, env.v4Server.Accounts, 1) - assert.NotNil(t, env.v4Server.Accounts[priv.Public().Base58()]) + + tokenAcc, _ := generateTokenAccount(ed25519.PrivateKey(priv)) + assert.NotNil(t, env.v4Server.Accounts[PublicKey(tokenAcc).Base58()]) env.v4Server.Mux.Unlock() } diff --git a/client/internal.go b/client/internal.go index 03c0a5a..f276727 100644 --- a/client/internal.go +++ b/client/internal.go @@ -252,7 +252,8 @@ func (c *InternalClient) CreateSolanaAccount(ctx context.Context, key PrivateKey return ErrNoSubsidizer } - publicKey := ed25519.PublicKey(key.Public()) + owner := ed25519.PublicKey(key.Public()) + tokenAcc, tokenAccKey := generateTokenAccount(ed25519.PrivateKey(key)) tokenProgram := config.TokenProgram.Value var subsidizerID ed25519.PublicKey @@ -272,17 +273,17 @@ func (c *InternalClient) CreateSolanaAccount(ctx context.Context, key PrivateKey } tx := solana.NewTransaction(subsidizerID, - system.CreateAccount(subsidizerID, publicKey, tokenProgram, minBalance, token.AccountSize), - token.InitializeAccount(publicKey, config.Token.Value, publicKey), - token.SetAuthority(publicKey, publicKey, subsidizerID, token.AuthorityTypeCloseAccount), + system.CreateAccount(subsidizerID, tokenAcc, tokenProgram, minBalance, token.AccountSize), + token.InitializeAccount(tokenAcc, config.Token.Value, owner), + token.SetAuthority(tokenAcc, owner, subsidizerID, token.AuthorityTypeCloseAccount), ) tx.SetBlockhash(recentBlockhash) var signers []ed25519.PrivateKey if subsidizer != nil { - signers = []ed25519.PrivateKey{ed25519.PrivateKey(subsidizer), ed25519.PrivateKey(key)} + signers = []ed25519.PrivateKey{ed25519.PrivateKey(subsidizer), ed25519.PrivateKey(key), tokenAccKey} } else { - signers = []ed25519.PrivateKey{ed25519.PrivateKey(key)} + signers = []ed25519.PrivateKey{ed25519.PrivateKey(key), tokenAccKey} } err = tx.Sign(signers...) if err != nil { diff --git a/client/internal_test.go b/client/internal_test.go index d1a3c99..a950fa6 100644 --- a/client/internal_test.go +++ b/client/internal_test.go @@ -488,15 +488,16 @@ func TestInternal_SolanaAccountRoundTrip(t *testing.T) { priv, err := NewPrivateKey() require.NoError(t, err) + tokenAcc, _ := generateTokenAccount(ed25519.PrivateKey(priv)) - accountInfo, err := env.internal.GetSolanaAccountInfo(context.Background(), priv.Public(), commonpbv4.Commitment_SINGLE) + accountInfo, err := env.internal.GetSolanaAccountInfo(context.Background(), PublicKey(tokenAcc), commonpbv4.Commitment_SINGLE) assert.Nil(t, accountInfo) assert.Equal(t, ErrAccountDoesNotExist, err) assert.NoError(t, env.internal.CreateSolanaAccount(context.Background(), priv, commonpbv4.Commitment_SINGLE, nil)) assert.Equal(t, ErrAccountExists, env.internal.CreateSolanaAccount(context.Background(), priv, commonpbv4.Commitment_SINGLE, nil)) - accountInfo, err = env.internal.GetSolanaAccountInfo(context.Background(), priv.Public(), commonpbv4.Commitment_SINGLE) + accountInfo, err = env.internal.GetSolanaAccountInfo(context.Background(), PublicKey(tokenAcc), commonpbv4.Commitment_SINGLE) assert.NoError(t, err) assert.NotNil(t, accountInfo) assert.EqualValues(t, 10, accountInfo.Balance) @@ -509,26 +510,27 @@ func TestInternal_SolanaAccountRoundTrip(t *testing.T) { tx := solana.Transaction{} require.NoError(t, tx.Unmarshal(createReq.Transaction.Value)) - assert.Len(t, tx.Signatures, 2) + assert.Len(t, tx.Signatures, 3) ed25519.Verify(ed25519.PublicKey(priv.Public()), tx.Message.Marshal(), tx.Signatures[1][:]) + ed25519.Verify(tokenAcc, tx.Message.Marshal(), tx.Signatures[2][:]) sysCreate, err := system.DecompileCreateAccount(tx.Message, 0) require.NoError(t, err) assert.Equal(t, subsidizer, sysCreate.Funder) - assert.EqualValues(t, priv.Public(), sysCreate.Address) + assert.EqualValues(t, tokenAcc, sysCreate.Address) assert.Equal(t, tokenProgram, sysCreate.Owner) assert.Equal(t, testserver.MinBalanceForRentException, sysCreate.Lamports) assert.Equal(t, token.AccountSize, int(sysCreate.Size)) tokenInit, err := token.DecompileInitializeAccount(tx.Message, 1) require.NoError(t, err) - assert.EqualValues(t, priv.Public(), tokenInit.Account) + assert.EqualValues(t, tokenAcc, tokenInit.Account) assert.Equal(t, tokenKey, tokenInit.Mint) assert.EqualValues(t, priv.Public(), tokenInit.Owner) setAuth, err := token.DecompileSetAuthority(tx.Message, 2) require.NoError(t, err) - assert.EqualValues(t, priv.Public(), setAuth.Account) + assert.EqualValues(t, tokenAcc, setAuth.Account) assert.EqualValues(t, priv.Public(), setAuth.CurrentAuthority) assert.Equal(t, subsidizer, setAuth.NewAuthority) assert.Equal(t, token.AuthorityTypeCloseAccount, setAuth.Type) @@ -542,6 +544,7 @@ func TestInternal_CreateNoServiceSubsidizer(t *testing.T) { priv, err := NewPrivateKey() require.NoError(t, err) + tokenAcc, _ := generateTokenAccount(ed25519.PrivateKey(priv)) err = env.internal.CreateSolanaAccount(context.Background(), priv, commonpbv4.Commitment_SINGLE, nil) require.Equal(t, ErrNoSubsidizer, err) @@ -558,26 +561,28 @@ func TestInternal_CreateNoServiceSubsidizer(t *testing.T) { tx := solana.Transaction{} require.NoError(t, tx.Unmarshal(createReq.Transaction.Value)) - assert.Len(t, tx.Signatures, 2) + assert.Len(t, tx.Signatures, 3) + ed25519.Verify(ed25519.PublicKey(subsidizer.Public()), tx.Message.Marshal(), tx.Signatures[0][:]) ed25519.Verify(ed25519.PublicKey(priv.Public()), tx.Message.Marshal(), tx.Signatures[1][:]) + ed25519.Verify(tokenAcc, tx.Message.Marshal(), tx.Signatures[2][:]) sysCreate, err := system.DecompileCreateAccount(tx.Message, 0) require.NoError(t, err) assert.EqualValues(t, subsidizer.Public(), sysCreate.Funder) - assert.EqualValues(t, priv.Public(), sysCreate.Address) + assert.EqualValues(t, tokenAcc, sysCreate.Address) assert.Equal(t, tokenProgram, sysCreate.Owner) assert.Equal(t, testserver.MinBalanceForRentException, sysCreate.Lamports) assert.Equal(t, token.AccountSize, int(sysCreate.Size)) tokenInit, err := token.DecompileInitializeAccount(tx.Message, 1) require.NoError(t, err) - assert.EqualValues(t, priv.Public(), tokenInit.Account) + assert.EqualValues(t, tokenAcc, tokenInit.Account) assert.Equal(t, tokenKey, tokenInit.Mint) assert.EqualValues(t, priv.Public(), tokenInit.Owner) setAuth, err := token.DecompileSetAuthority(tx.Message, 2) require.NoError(t, err) - assert.EqualValues(t, priv.Public(), setAuth.Account) + assert.EqualValues(t, tokenAcc, setAuth.Account) assert.EqualValues(t, priv.Public(), setAuth.CurrentAuthority) assert.EqualValues(t, subsidizer.Public(), setAuth.NewAuthority) assert.Equal(t, token.AuthorityTypeCloseAccount, setAuth.Type) @@ -951,23 +956,24 @@ func TestInternal_RequestAirdrop(t *testing.T) { env, cleanup := setup(t) defer cleanup() - sender, err := NewPrivateKey() + priv, err := NewPrivateKey() require.NoError(t, err) + tokenAcc, _ := generateTokenAccount(ed25519.PrivateKey(priv)) // Account doesn't exist - txID, err := env.internal.RequestAirdrop(context.Background(), sender.Public(), 10, commonpbv4.Commitment_SINGLE) + txID, err := env.internal.RequestAirdrop(context.Background(), PublicKey(tokenAcc), 10, commonpbv4.Commitment_SINGLE) assert.Equal(t, ErrAccountDoesNotExist, err) assert.Nil(t, txID) setServiceConfigResp(t, env.v4Server, true) - require.NoError(t, env.internal.CreateSolanaAccount(context.Background(), sender, commonpbv4.Commitment_SINGLE, nil)) + require.NoError(t, env.internal.CreateSolanaAccount(context.Background(), priv, commonpbv4.Commitment_SINGLE, nil)) // Too much money - txID, err = env.internal.RequestAirdrop(context.Background(), sender.Public(), testserver.MaxAirdrop+1, commonpbv4.Commitment_SINGLE) + txID, err = env.internal.RequestAirdrop(context.Background(), PublicKey(tokenAcc), testserver.MaxAirdrop+1, commonpbv4.Commitment_SINGLE) assert.Equal(t, ErrInsufficientBalance, err) assert.Nil(t, txID) - txID, err = env.internal.RequestAirdrop(context.Background(), sender.Public(), testserver.MaxAirdrop, commonpbv4.Commitment_SINGLE) + txID, err = env.internal.RequestAirdrop(context.Background(), PublicKey(tokenAcc), testserver.MaxAirdrop, commonpbv4.Commitment_SINGLE) require.NoError(t, err) assert.NotNil(t, txID) } diff --git a/client/utils.go b/client/utils.go new file mode 100644 index 0000000..afc5957 --- /dev/null +++ b/client/utils.go @@ -0,0 +1,12 @@ +package client + +import ( + "crypto/ed25519" + "crypto/sha256" +) + +func generateTokenAccount(ownerKey ed25519.PrivateKey) (ed25519.PublicKey, ed25519.PrivateKey) { + tokenAccSeed := sha256.Sum256(ownerKey) + tokenAccKey := ed25519.NewKeyFromSeed(tokenAccSeed[:]) + return tokenAccKey.Public().(ed25519.PublicKey), tokenAccKey +}