Skip to content
This repository has been archived by the owner on Jan 16, 2023. It is now read-only.

Commit

Permalink
client: add get balance resolution; adjust account resolution testing
Browse files Browse the repository at this point in the history
GitOrigin-RevId: 63a1a897e6e1d0fafc010541776fdef67526244b
  • Loading branch information
kikengineering committed Dec 8, 2020
1 parent 754610a commit 982915f
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 67 deletions.
55 changes: 37 additions & 18 deletions client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -198,10 +198,10 @@ func WithDefaultCommitment(defaultCommitment commonpbv4.Commitment) ClientOption
}

type solanaOpts struct {
commitment commonpbv4.Commitment
senderResolution AccountResolution
destResolution AccountResolution
subsidizer PrivateKey
commitment commonpbv4.Commitment
accountResolution AccountResolution
destResolution AccountResolution
subsidizer PrivateKey
}

// ClientOption configures a solana-related function call.
Expand All @@ -214,10 +214,11 @@ func WithCommitment(commitment commonpbv4.Commitment) SolanaOption {
}
}

// WithSenderResolution specifies an account resolution to use for a Kin 4 payment/earn batch sender.
func WithSenderResolution(resolution AccountResolution) SolanaOption {
// WithAccountResolution specifies an account resolution to use for a Kin 4 request.
// In the case of payments/earn batches, the specified resolution will be used only for the sender.
func WithAccountResolution(resolution AccountResolution) SolanaOption {
return func(o *solanaOpts) {
o.senderResolution = resolution
o.accountResolution = resolution
}
}

Expand Down Expand Up @@ -379,12 +380,30 @@ func (c *client) GetBalance(ctx context.Context, account PublicKey, opts ...Sola
return 0, errors.Errorf("unsupported kin version: %d", c.opts.kinVersion)
}

solanaOpts := solanaOpts{commitment: c.opts.defaultCommitment}
solanaOpts := solanaOpts{
commitment: c.opts.defaultCommitment,
accountResolution: AccountResolutionPreferred,
}
for _, o := range opts {
o(&solanaOpts)
}

accountInfo, err := c.internal.GetSolanaAccountInfo(ctx, account, solanaOpts.commitment)
if err != nil {
if err == ErrAccountDoesNotExist && solanaOpts.accountResolution == AccountResolutionPreferred {
tokenAccounts, tokenErr := c.resolveTokenAccounts(ctx, account)
if tokenErr != nil {
return 0, tokenErr
}

if len(tokenAccounts) == 0 {
return 0, ErrAccountDoesNotExist
}

accountInfo, err = c.internal.GetSolanaAccountInfo(ctx, tokenAccounts[0], solanaOpts.commitment)
if err != nil {
return 0, err
}
} else if err != nil {
return 0, err
}

Expand Down Expand Up @@ -429,9 +448,9 @@ func (c *client) SubmitPayment(ctx context.Context, payment Payment, opts ...Sol
}

solanaOpts := solanaOpts{
commitment: c.opts.defaultCommitment,
senderResolution: AccountResolutionPreferred,
destResolution: AccountResolutionPreferred,
commitment: c.opts.defaultCommitment,
accountResolution: AccountResolutionPreferred,
destResolution: AccountResolutionPreferred,
}
for _, o := range opts {
o(&solanaOpts)
Expand Down Expand Up @@ -614,9 +633,9 @@ func (c *client) SubmitEarnBatch(ctx context.Context, batch EarnBatch, opts ...S
var config *transactionpbv4.GetServiceConfigResponse

solanaOpts := solanaOpts{
commitment: c.opts.defaultCommitment,
senderResolution: AccountResolutionPreferred,
destResolution: AccountResolutionPreferred,
commitment: c.opts.defaultCommitment,
accountResolution: AccountResolutionPreferred,
destResolution: AccountResolutionPreferred,
}
for _, o := range opts {
o(&solanaOpts)
Expand Down Expand Up @@ -646,7 +665,7 @@ func (c *client) SubmitEarnBatch(ctx context.Context, batch EarnBatch, opts ...S
return result, ErrNoSubsidizer
}

batches = c.partitionSolanaEarns(batch, solanaOpts.senderResolution)
batches = c.partitionSolanaEarns(batch, solanaOpts.accountResolution)
}

lastProcessedBatch := -1
Expand Down Expand Up @@ -751,7 +770,7 @@ func (c *client) submitPaymentWithResolution(ctx context.Context, payment Paymen

if result.Errors.TxError == ErrAccountDoesNotExist {
var resubmit bool
if solanaOpts.senderResolution == AccountResolutionPreferred {
if solanaOpts.accountResolution == AccountResolutionPreferred {
tokenAccounts, err := c.resolveTokenAccounts(ctx, payment.Sender.Public())
if err != nil {
return result, err
Expand Down Expand Up @@ -847,7 +866,7 @@ func (c *client) submitEarnBatchWithResolution(ctx context.Context, batch EarnBa

if result.Errors.TxError == ErrAccountDoesNotExist {
var resubmit bool
if solanaOpts.senderResolution == AccountResolutionPreferred {
if solanaOpts.accountResolution == AccountResolutionPreferred {
tokenAccounts, err := c.resolveTokenAccounts(ctx, batch.Sender.Public())
if err != nil {
return result, err
Expand Down
71 changes: 26 additions & 45 deletions client/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1123,6 +1123,15 @@ func TestClient_Kin4AccountManagement(t *testing.T) {
balance, err = env.client.GetBalance(context.Background(), PublicKey(tokenAcc))
assert.NoError(t, err)
assert.EqualValues(t, 10, balance)

// Test resolution options
balance, err = env.client.GetBalance(context.Background(), priv.Public(), WithAccountResolution(AccountResolutionExact))
assert.Equal(t, ErrAccountDoesNotExist, err)
assert.Zero(t, balance)

balance, err = env.client.GetBalance(context.Background(), priv.Public())
require.NoError(t, err)
assert.EqualValues(t, 10, balance)
}

func TestClient_Kin4SubmitPayment(t *testing.T) {
Expand Down Expand Up @@ -1363,13 +1372,11 @@ func TestClient_Kin4SubmitPaymentKin4AccountResolution(t *testing.T) {
require.NoError(t, err)
dest, err := NewPrivateKey()
require.NoError(t, err)
resolvedSender, err := NewPrivateKey()
require.NoError(t, err)
resolvedDest, err := NewPrivateKey()
require.NoError(t, err)
resolvedSender, _ := generateTokenAccount(ed25519.PrivateKey(sender))
resolvedDest, _ := generateTokenAccount(ed25519.PrivateKey(dest))

setServiceConfigResp(t, env.v4Server, true)
for _, acc := range [][]byte{sender, dest, resolvedSender, resolvedDest} {
for _, acc := range [][]byte{sender, dest} {
require.NoError(t, env.client.CreateAccount(context.Background(), acc))
}

Expand All @@ -1391,21 +1398,9 @@ func TestClient_Kin4SubmitPaymentKin4AccountResolution(t *testing.T) {
},
},
}
env.v4Server.TokenAccounts = map[string][]*commonpbv4.SolanaAccountId{
sender.Public().Base58(): {
{
Value: resolvedSender.Public(),
},
},
dest.Public().Base58(): {
{
Value: resolvedDest.Public(),
},
},
}
env.v4Server.Mux.Unlock()

txID, err := env.client.SubmitPayment(context.Background(), p, WithSenderResolution(AccountResolutionPreferred), WithDestResolution(AccountResolutionPreferred))
txID, err := env.client.SubmitPayment(context.Background(), p, WithAccountResolution(AccountResolutionPreferred), WithDestResolution(AccountResolutionPreferred))
require.NoError(t, err)
assert.NotNil(t, txID)

Expand Down Expand Up @@ -1437,8 +1432,8 @@ func TestClient_Kin4SubmitPaymentKin4AccountResolution(t *testing.T) {
assert.EqualValues(t, sender.Public(), transferInstr.Source)
assert.EqualValues(t, dest.Public(), transferInstr.Destination)
} else {
assert.EqualValues(t, resolvedSender.Public(), transferInstr.Source)
assert.EqualValues(t, resolvedDest.Public(), transferInstr.Destination)
assert.EqualValues(t, resolvedSender, transferInstr.Source)
assert.EqualValues(t, resolvedDest, transferInstr.Destination)
}
assert.EqualValues(t, sender.Public(), transferInstr.Owner)
assert.EqualValues(t, p.Quarks, transferInstr.Amount)
Expand All @@ -1456,10 +1451,9 @@ func TestClient_Kin4SubmitPaymentKin4AccountResolution(t *testing.T) {
},
},
}
env.v4Server.TokenAccounts = map[string][]*commonpbv4.SolanaAccountId{}
env.v4Server.Mux.Unlock()

txID, err = env.client.SubmitPayment(context.Background(), p, WithSenderResolution(AccountResolutionExact), WithDestResolution(AccountResolutionExact))
txID, err = env.client.SubmitPayment(context.Background(), p, WithAccountResolution(AccountResolutionExact), WithDestResolution(AccountResolutionExact))
assert.EqualValues(t, ErrAccountDoesNotExist, err)
assert.NotNil(t, txID)
}
Expand Down Expand Up @@ -1766,7 +1760,7 @@ func TestClient_Kin4SubmitEarnBatchAccountResolution(t *testing.T) {

sender, err := NewPrivateKey()
require.NoError(t, err)
resolvedSender, err := NewPrivateKey()
resolvedSender, _ := generateTokenAccount(ed25519.PrivateKey(sender))
require.NoError(t, err)

// Test Preferred Account Resolution
Expand All @@ -1780,17 +1774,11 @@ func TestClient_Kin4SubmitEarnBatchAccountResolution(t *testing.T) {
},
},
}
env.v4Server.TokenAccounts = map[string][]*commonpbv4.SolanaAccountId{
sender.Public().Base58(): {
{
Value: resolvedSender.Public(),
},
},
}
env.v4Server.Mux.Unlock()

earns := make([]Earn, 20)
earnAccounts := make([]PrivateKey, 20)
resolvedEarnAccounts := make([]PrivateKey, 20)
resolvedEarnAccounts := make([]PublicKey, 20)
for i := 0; i < len(earnAccounts); i++ {
dest, err := NewPrivateKey()
require.NoError(t, err)
Expand All @@ -1801,27 +1789,21 @@ func TestClient_Kin4SubmitEarnBatchAccountResolution(t *testing.T) {
Quarks: int64(i) + 1,
}

resolvedDest, err := NewPrivateKey()
resolvedDest, _ := generateTokenAccount(ed25519.PrivateKey(dest))
require.NoError(t, err)
resolvedEarnAccounts[i] = resolvedDest
env.v4Server.TokenAccounts[dest.Public().Base58()] = []*commonpbv4.SolanaAccountId{
{
Value: resolvedDest.Public(),
},
}
resolvedEarnAccounts[i] = PublicKey(resolvedDest)
}
env.v4Server.Mux.Unlock()

setServiceConfigResp(t, env.v4Server, true)
for _, acc := range append([]PrivateKey{sender, resolvedSender}, append(earnAccounts, resolvedEarnAccounts...)...) {
for _, acc := range append([]PrivateKey{sender}, earnAccounts...) {
require.NoError(t, env.client.CreateAccount(context.Background(), acc))
}
b := EarnBatch{
Sender: sender,
Earns: earns,
}

result, err := env.client.SubmitEarnBatch(context.Background(), b, WithSenderResolution(AccountResolutionPreferred), WithDestResolution(AccountResolutionPreferred))
result, err := env.client.SubmitEarnBatch(context.Background(), b, WithAccountResolution(AccountResolutionPreferred), WithDestResolution(AccountResolutionPreferred))
assert.NoError(t, err)

env.v4Server.Mux.Lock()
Expand Down Expand Up @@ -1873,8 +1855,8 @@ func TestClient_Kin4SubmitEarnBatchAccountResolution(t *testing.T) {

earn := b.Earns[batchIndex*batchSize+j]
if resolved {
require.EqualValues(t, resolvedSender.Public(), transferInstr.Source)
require.EqualValues(t, resolvedEarnAccounts[batchIndex*batchSize+j].Public(), transferInstr.Destination)
require.EqualValues(t, resolvedSender, transferInstr.Source)
require.EqualValues(t, resolvedEarnAccounts[batchIndex*batchSize+j], transferInstr.Destination)
} else {
require.EqualValues(t, sender.Public(), transferInstr.Source)
require.EqualValues(t, earnAccounts[batchIndex*batchSize+j].Public(), transferInstr.Destination)
Expand All @@ -1897,10 +1879,9 @@ func TestClient_Kin4SubmitEarnBatchAccountResolution(t *testing.T) {
},
},
}
env.v4Server.TokenAccounts = map[string][]*commonpbv4.SolanaAccountId{}
env.v4Server.Mux.Unlock()

result, err = env.client.SubmitEarnBatch(context.Background(), b, WithSenderResolution(AccountResolutionExact), WithDestResolution(AccountResolutionExact))
result, err = env.client.SubmitEarnBatch(context.Background(), b, WithAccountResolution(AccountResolutionExact), WithDestResolution(AccountResolutionExact))
assert.Equal(t, err, ErrAccountDoesNotExist)

env.v4Server.Mux.Lock()
Expand Down
20 changes: 16 additions & 4 deletions client/testserver/test_v4_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/kinecosystem/agora-api/genproto/airdrop/v4"
"github.com/kinecosystem/agora-common/solana"
"github.com/kinecosystem/agora-common/solana/system"
"github.com/kinecosystem/agora-common/solana/token"
"github.com/mr-tron/base58"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
Expand Down Expand Up @@ -77,8 +78,15 @@ func (t *V4Server) CreateAccount(ctx context.Context, req *accountpbv4.CreateAcc
return nil, status.Error(codes.InvalidArgument, "invalid Sys::CreateAccount instruction")
}

accountID := base58.Encode(sysCreate.Address)
if info, ok := t.Accounts[accountID]; ok {
tokenInit, err := token.DecompileInitializeAccount(tx.Message, 1)
if err != nil {
return nil, status.Error(codes.InvalidArgument, "invalid Token::InitializeAccount instruction")
}

tokenAccID := base58.Encode(sysCreate.Address)
ownerID := base58.Encode(tokenInit.Owner)

if info, ok := t.Accounts[tokenAccID]; ok {
return &accountpbv4.CreateAccountResponse{
Result: accountpbv4.CreateAccountResponse_EXISTS,
AccountInfo: proto.Clone(info).(*accountpbv4.AccountInfo),
Expand All @@ -89,7 +97,9 @@ func (t *V4Server) CreateAccount(ctx context.Context, req *accountpbv4.CreateAcc
AccountId: &commonpbv4.SolanaAccountId{Value: sysCreate.Address},
Balance: 10,
}
t.Accounts[accountID] = accountInfo
t.Accounts[tokenAccID] = accountInfo
t.TokenAccounts[ownerID] = append(t.TokenAccounts[ownerID], &commonpbv4.SolanaAccountId{Value: sysCreate.Address})

return &accountpbv4.CreateAccountResponse{
AccountInfo: accountInfo,
}, nil
Expand Down Expand Up @@ -123,7 +133,9 @@ func (t *V4Server) ResolveTokenAccounts(ctx context.Context, req *accountpbv4.Re
return nil, err
}

accounts, ok := t.TokenAccounts[base58.Encode(req.AccountId.Value)]
ownerID := base58.Encode(req.AccountId.Value)

accounts, ok := t.TokenAccounts[ownerID]
if !ok {
return &accountpbv4.ResolveTokenAccountsResponse{}, nil
}
Expand Down

0 comments on commit 982915f

Please sign in to comment.