Skip to content

Commit

Permalink
Merge pull request #6264 from AndriiDiachuk/add-rest-endpoint-GetAcco…
Browse files Browse the repository at this point in the history
…untKeys

[Access] Add REST endpoint to get an accounts public keys
  • Loading branch information
Guitarheroua authored Aug 1, 2024
2 parents d719f5f + 27c7349 commit 291965e
Show file tree
Hide file tree
Showing 10 changed files with 415 additions and 10 deletions.
33 changes: 33 additions & 0 deletions engine/access/rest/apiproxy/rest_proxy_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,39 @@ func (r *RestProxyHandler) GetAccountBalanceAtBlockHeight(ctx context.Context, a

}

// GetAccountKeys returns account keys by account address and block height.
func (r *RestProxyHandler) GetAccountKeys(ctx context.Context, address flow.Address, height uint64) ([]flow.AccountPublicKey, error) {
upstream, closer, err := r.FaultTolerantClient()
if err != nil {
return nil, err
}
defer closer.Close()

getAccountKeysAtBlockHeightRequest := &accessproto.GetAccountKeysAtBlockHeightRequest{
Address: address.Bytes(),
BlockHeight: height,
}

accountKeyResponse, err := upstream.GetAccountKeysAtBlockHeight(ctx, getAccountKeysAtBlockHeightRequest)
r.log("upstream", "GetAccountKeysAtBlockHeight", err)

if err != nil {
return nil, err
}

accountKeys := make([]flow.AccountPublicKey, len(accountKeyResponse.GetAccountKeys()))
for i, key := range accountKeyResponse.GetAccountKeys() {
accountKey, err := convert.MessageToAccountKey(key)
if err != nil {
return nil, err
}

accountKeys[i] = *accountKey
}

return accountKeys, nil
}

// GetAccountKeyByIndex returns account key by account address, key index and block height.
func (r *RestProxyHandler) GetAccountKeyByIndex(ctx context.Context, address flow.Address, keyIndex uint32, height uint64) (*flow.AccountPublicKey, error) {
upstream, closer, err := r.FaultTolerantClient()
Expand Down
19 changes: 16 additions & 3 deletions engine/access/rest/models/account.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ func (a *Account) Build(flowAccount *flow.Account, link LinkGenerator, expand ma
a.Expandable = &AccountExpandable{}

if expand[expandableKeys] {
var keys AccountPublicKeys
var keys AccountKeys
keys.Build(flowAccount.Keys)
a.Keys = keys
} else {
Expand Down Expand Up @@ -54,9 +54,9 @@ func (a *AccountPublicKey) Build(k flow.AccountPublicKey) {
a.Revoked = k.Revoked
}

type AccountPublicKeys []AccountPublicKey
type AccountKeys []AccountPublicKey

func (a *AccountPublicKeys) Build(accountKeys []flow.AccountPublicKey) {
func (a *AccountKeys) Build(accountKeys []flow.AccountPublicKey) {
keys := make([]AccountPublicKey, len(accountKeys))
for i, k := range accountKeys {
var key AccountPublicKey
Expand All @@ -67,6 +67,19 @@ func (a *AccountPublicKeys) Build(accountKeys []flow.AccountPublicKey) {
*a = keys
}

// Build function use model AccountPublicKeys type for GetAccountKeys call
// AccountPublicKeys is an auto-generated type from the openapi spec
func (a *AccountPublicKeys) Build(accountKeys []flow.AccountPublicKey) {
keys := make([]AccountPublicKey, len(accountKeys))
for i, k := range accountKeys {
var key AccountPublicKey
key.Build(k)
keys[i] = key
}

a.Keys = keys
}

func (b *AccountBalance) Build(balance uint64) {
b.Balance = util.FromUint(balance)
}
13 changes: 13 additions & 0 deletions engine/access/rest/models/model_account_public_keys.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/*
* Access API
*
* No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen)
*
* API version: 1.0.0
* Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git)
*/
package models

type AccountPublicKeys struct {
Keys []AccountPublicKey `json:"keys"`
}
45 changes: 45 additions & 0 deletions engine/access/rest/request/get_account_keys.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package request

import (
"github.com/onflow/flow-go/model/flow"
)

type GetAccountKeys struct {
Address flow.Address
Height uint64
}

func (g *GetAccountKeys) Build(r *Request) error {
return g.Parse(
r.GetVar(addressVar),
r.GetQueryParam(blockHeightQuery),
r.Chain,
)
}

func (g *GetAccountKeys) Parse(
rawAddress string,
rawHeight string,
chain flow.Chain,
) error {
address, err := ParseAddress(rawAddress, chain)
if err != nil {
return err
}

var height Height
err = height.Parse(rawHeight)
if err != nil {
return err
}

g.Address = address
g.Height = height.Flow()

// default to last block
if g.Height == EmptyHeight {
g.Height = SealedHeight
}

return nil
}
52 changes: 52 additions & 0 deletions engine/access/rest/request/get_account_keys_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package request

import (
"fmt"
"testing"

"github.com/stretchr/testify/assert"

"github.com/onflow/flow-go/model/flow"
)

func Test_GetAccountKeys_InvalidParse(t *testing.T) {
var getAccountKeys GetAccountKeys

tests := []struct {
address string
height string
err string
}{
{"", "", "invalid address"},
{"f8d6e0586b0a20c7", "-1", "invalid height format"},
}

chain := flow.Localnet.Chain()
for i, test := range tests {
err := getAccountKeys.Parse(test.address, test.height, chain)
assert.EqualError(t, err, test.err, fmt.Sprintf("test #%d failed", i))
}
}

func Test_GetAccountKeys_ValidParse(t *testing.T) {
var getAccountKeys GetAccountKeys

addr := "f8d6e0586b0a20c7"
chain := flow.Localnet.Chain()
err := getAccountKeys.Parse(addr, "", chain)
assert.NoError(t, err)
assert.Equal(t, getAccountKeys.Address.String(), addr)
assert.Equal(t, getAccountKeys.Height, SealedHeight)

err = getAccountKeys.Parse(addr, "100", chain)
assert.NoError(t, err)
assert.Equal(t, getAccountKeys.Height, uint64(100))

err = getAccountKeys.Parse(addr, sealed, chain)
assert.NoError(t, err)
assert.Equal(t, getAccountKeys.Height, SealedHeight)

err = getAccountKeys.Parse(addr, final, chain)
assert.NoError(t, err)
assert.Equal(t, getAccountKeys.Height, FinalHeight)
}
6 changes: 6 additions & 0 deletions engine/access/rest/request/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,12 @@ func (rd *Request) GetAccountBalanceRequest() (GetAccountBalance, error) {
return req, err
}

func (rd *Request) GetAccountKeysRequest() (GetAccountKeys, error) {
var req GetAccountKeys
err := req.Build(rd)
return req, err
}

func (rd *Request) GetAccountKeyRequest() (GetAccountKey, error) {
var req GetAccountKey
err := req.Build(rd)
Expand Down
33 changes: 32 additions & 1 deletion engine/access/rest/routes/account_keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
)

// GetAccountKeyByIndex handler retrieves an account key by address and index and returns the response
func GetAccountKeyByIndex(r *request.Request, backend access.API, link models.LinkGenerator) (interface{}, error) {
func GetAccountKeyByIndex(r *request.Request, backend access.API, _ models.LinkGenerator) (interface{}, error) {
req, err := r.GetAccountKeyRequest()
if err != nil {
return nil, models.NewBadRequestError(err)
Expand Down Expand Up @@ -37,3 +37,34 @@ func GetAccountKeyByIndex(r *request.Request, backend access.API, link models.Li
response.Build(*accountKey)
return response, nil
}

// GetAccountKeys handler retrieves an account keys by address and returns the response
func GetAccountKeys(r *request.Request, backend access.API, _ models.LinkGenerator) (interface{}, error) {
req, err := r.GetAccountKeysRequest()
if err != nil {
return nil, models.NewBadRequestError(err)
}

// In case we receive special height values 'final' and 'sealed',
// fetch that height and overwrite request with it.
isSealed := req.Height == request.SealedHeight
isFinalized := req.Height == request.FinalHeight
if isFinalized || isSealed {
header, _, err := backend.GetLatestBlockHeader(r.Context(), isSealed)
if err != nil {
err := fmt.Errorf("block with height: %d does not exist", req.Height)
return nil, models.NewNotFoundError(err.Error(), err)
}
req.Height = header.Height
}

accountKeys, err := backend.GetAccountKeysAtBlockHeight(r.Context(), req.Address, req.Height)
if err != nil {
err = fmt.Errorf("failed to get account keys, reason: %w", err)
return nil, models.NewNotFoundError(err.Error(), err)
}

var response models.AccountPublicKeys
response.Build(accountKeys)
return response, nil
}
Loading

0 comments on commit 291965e

Please sign in to comment.