Skip to content

Commit

Permalink
[PVM] Add missing client methods, add encoding arg to block requests (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
evlekht authored Nov 4, 2024
1 parent 10f07e6 commit c19e8c0
Show file tree
Hide file tree
Showing 3 changed files with 174 additions and 10 deletions.
10 changes: 10 additions & 0 deletions api/camino.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Copyright (C) 2022-2024, Chain4Travel AG. All rights reserved.
// See the file LICENSE for licensing terms.

package api

import "github.com/ava-labs/avalanchego/utils/formatting"

type Encoding struct {
Encoding formatting.Encoding `json:"encoding"`
}
125 changes: 125 additions & 0 deletions vms/platformvm/camino_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,17 @@ package platformvm

import (
"context"
"fmt"

"github.com/ava-labs/avalanchego/api"
"github.com/ava-labs/avalanchego/ids"
"github.com/ava-labs/avalanchego/utils/formatting"
"github.com/ava-labs/avalanchego/utils/formatting/address"
"github.com/ava-labs/avalanchego/utils/json"
"github.com/ava-labs/avalanchego/utils/rpc"
platformapi "github.com/ava-labs/avalanchego/vms/platformvm/api"
"github.com/ava-labs/avalanchego/vms/platformvm/state"
"github.com/ava-labs/avalanchego/vms/secp256k1fx"
)

type CaminoClient interface {
Expand All @@ -18,6 +26,11 @@ type CaminoClient interface {
GetMultisigAlias(ctx context.Context, multisigAddress string, options ...rpc.Option) (*GetMultisigAliasReply, error)

GetAllDepositOffers(ctx context.Context, getAllDepositOffersArgs *GetAllDepositOffersArgs, options ...rpc.Option) (*GetAllDepositOffersReply, error)

GetRegisteredShortIDLink(ctx context.Context, addrStr ids.ShortID, options ...rpc.Option) (string, error)
GetLastAcceptedBlock(ctx context.Context, encoding formatting.Encoding, options ...rpc.Option) (any, error)
GetBlockAtHeight(ctx context.Context, height uint32, encoding formatting.Encoding, options ...rpc.Option) (any, error)
GetClaimables(ctx context.Context, owners []*secp256k1fx.OutputOwners, options ...rpc.Option) ([]*state.Claimable, error)
}

func (c *client) GetConfiguration(ctx context.Context, options ...rpc.Option) (*GetConfigurationReply, error) {
Expand All @@ -39,3 +52,115 @@ func (c *client) GetAllDepositOffers(ctx context.Context, getAllDepositOffersArg
err := c.requester.SendRequest(ctx, "platform.getAllDepositOffers", &getAllDepositOffersArgs, res, options...)
return res, err
}

func (c *client) GetRegisteredShortIDLink(ctx context.Context, addrStr ids.ShortID, options ...rpc.Option) (string, error) {
res := &api.JSONAddress{}
err := c.requester.SendRequest(ctx, "platform.getMultisigAlias", &api.JSONAddress{
Address: addrStr.String(),
}, res, options...)
return res.Address, err
}

func (c *client) GetLastAcceptedBlock(ctx context.Context, encoding formatting.Encoding, options ...rpc.Option) (any, error) {
res := &api.GetBlockResponse{}
err := c.requester.SendRequest(ctx, "platform.getLastAcceptedBlock", &api.Encoding{
Encoding: encoding,
}, res, options...)
return res.Block, err
}

func (c *client) GetBlockAtHeight(ctx context.Context, height uint32, encoding formatting.Encoding, options ...rpc.Option) (any, error) {
res := &api.GetBlockResponse{}
err := c.requester.SendRequest(ctx, "platform.getBlockAtHeight", &GetBlockAtHeightArgs{
Height: height,
Encoding: encoding,
}, res, options...)
return res.Block, err
}

func (c *client) GetClaimables(ctx context.Context, owners []*secp256k1fx.OutputOwners, options ...rpc.Option) ([]*state.Claimable, error) {
res := &GetClaimablesReply{}
if err := c.requester.SendRequest(ctx, "platform.getClaimables", &GetClaimablesArgs{
Owners: apiOwnersFromSECP(owners),
}, res, options...); err != nil {
return nil, err
}
return claimablesFromAPI(res.Claimables)
}

func claimablesFromAPI(apiClaimables []APIClaimable) ([]*state.Claimable, error) {
claimables := make([]*state.Claimable, len(apiClaimables))
for i := range claimables {
claimable, err := claimableFromAPI(&apiClaimables[i])
if err != nil {
return nil, err
}
claimables[i] = &claimable
}
return claimables, nil
}

func claimableFromAPI(apiClaimable *APIClaimable) (state.Claimable, error) {
claimableOwner, err := secpOwnerFromAPI(&apiClaimable.RewardOwner)
if err != nil {
return state.Claimable{}, err
}
return state.Claimable{
Owner: claimableOwner,
ValidatorReward: uint64(apiClaimable.ValidatorRewards),
ExpiredDepositReward: uint64(apiClaimable.ExpiredDepositRewards),
}, nil
}

func apiOwnersFromSECP(secpOwners []*secp256k1fx.OutputOwners) []platformapi.Owner {
owners := make([]platformapi.Owner, len(secpOwners))
for i := range owners {
owners[i] = *apiOwnerFromSECP(secpOwners[i])
}
return owners
}

func apiOwnerFromSECP(secpOwner *secp256k1fx.OutputOwners) *platformapi.Owner {
apiOwner := &platformapi.Owner{
Locktime: json.Uint64(secpOwner.Locktime),
Threshold: json.Uint32(secpOwner.Threshold),
Addresses: make([]string, len(secpOwner.Addrs)),
}
for i := range secpOwner.Addrs {
apiOwner.Addresses[i] = secpOwner.Addrs[i].String()
}
return apiOwner
}

func secpOwnerFromAPI(apiOwner *platformapi.Owner) (*secp256k1fx.OutputOwners, error) {
if len(apiOwner.Addresses) > 0 {
secpOwner := &secp256k1fx.OutputOwners{
Locktime: uint64(apiOwner.Locktime),
Threshold: uint32(apiOwner.Threshold),
Addrs: make([]ids.ShortID, len(apiOwner.Addresses)),
}
for i := range apiOwner.Addresses {
addr, err := parseAddr(apiOwner.Addresses[i])
if err != nil {
return nil, err
}
secpOwner.Addrs[i] = addr
}
secpOwner.Sort()
return secpOwner, nil
}
return nil, nil
}

func parseAddr(addrStr string) (ids.ShortID, error) {
addr, err1 := address.ParseToID(addrStr)
if err1 == nil {
return addr, nil
}
addr, err2 := ids.ShortFromString(addrStr)
if err2 != nil {
return ids.ShortEmpty, fmt.Errorf("failed to parse addr both as shortID (%s) and as bech32 (%s)",
err2, err1)
}
return addr, nil
}
49 changes: 39 additions & 10 deletions vms/platformvm/camino_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"github.com/ava-labs/avalanchego/vms/components/avax"
"github.com/ava-labs/avalanchego/vms/components/keystore"
as "github.com/ava-labs/avalanchego/vms/platformvm/addrstate"
"github.com/ava-labs/avalanchego/vms/platformvm/blocks"
"github.com/ava-labs/avalanchego/vms/platformvm/deposit"
"github.com/ava-labs/avalanchego/vms/platformvm/locked"
"github.com/ava-labs/avalanchego/vms/platformvm/state"
Expand Down Expand Up @@ -769,7 +770,7 @@ func (s *CaminoService) GetDeposits(_ *http.Request, args *GetDepositsArgs, repl
}

// GetLastAcceptedBlock returns the last accepted block
func (s *CaminoService) GetLastAcceptedBlock(r *http.Request, _ *struct{}, reply *api.GetBlockResponse) error {
func (s *CaminoService) GetLastAcceptedBlock(r *http.Request, args *api.Encoding, reply *api.GetBlockResponse) error {
s.vm.ctx.Log.Debug("Platform: GetLastAcceptedBlock called")

ctx := r.Context()
Expand All @@ -782,18 +783,29 @@ func (s *CaminoService) GetLastAcceptedBlock(r *http.Request, _ *struct{}, reply
return fmt.Errorf("couldn't get block with id %s: %w", lastAcceptedID, err)
}

block.InitCtx(s.vm.ctx)
reply.Encoding = formatting.JSON
reply.Block = block
reply.Encoding = args.Encoding

if args.Encoding == formatting.JSON {
block.InitCtx(s.vm.ctx)
reply.Block = block
return nil
}

reply.Block, err = formatting.Encode(args.Encoding, block.Bytes())
if err != nil {
return fmt.Errorf("couldn't encode block %s as string: %w", block.ID(), err)
}

return nil
}

type GetBlockAtHeight struct {
Height uint32 `json:"height"`
type GetBlockAtHeightArgs struct {
Encoding formatting.Encoding `json:"encoding"`
Height uint32 `json:"height"`
}

// GetBlockAtHeight returns block at given height
func (s *CaminoService) GetBlockAtHeight(r *http.Request, args *GetBlockAtHeight, reply *api.GetBlockResponse) error {
func (s *CaminoService) GetBlockAtHeight(r *http.Request, args *GetBlockAtHeightArgs, reply *api.GetBlockResponse) error {
s.vm.ctx.Log.Debug("Platform: GetBlockAtHeight called")

ctx := r.Context()
Expand All @@ -802,20 +814,37 @@ func (s *CaminoService) GetBlockAtHeight(r *http.Request, args *GetBlockAtHeight
return fmt.Errorf("couldn't get last accepted block ID: %w", err)
}

var desiredBlock blocks.Block

for {
block, err := s.vm.manager.GetStatelessBlock(blockID)
if err != nil {
return fmt.Errorf("couldn't get block with id %s: %w", blockID, err)
}
if block.Height() == uint64(args.Height) {
block.InitCtx(s.vm.ctx)
reply.Encoding = formatting.JSON
reply.Block = block
desiredBlock = block
break
}
blockID = block.Parent()
}

if desiredBlock == nil {
return fmt.Errorf("couldn't find block at height %d", args.Height)
}

reply.Encoding = args.Encoding

if args.Encoding == formatting.JSON {
desiredBlock.InitCtx(s.vm.ctx)
reply.Block = desiredBlock
return nil
}

reply.Block, err = formatting.Encode(args.Encoding, desiredBlock.Bytes())
if err != nil {
return fmt.Errorf("couldn't encode block %s as string: %w", desiredBlock.ID(), err)
}

return nil
}

Expand Down

0 comments on commit c19e8c0

Please sign in to comment.