github.com/MetalBlockchain/metalgo@v1.11.9/vms/example/xsvm/api/client.go (about)

     1  // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package api
     5  
     6  import (
     7  	"context"
     8  	"fmt"
     9  	"time"
    10  
    11  	"github.com/MetalBlockchain/metalgo/ids"
    12  	"github.com/MetalBlockchain/metalgo/utils/constants"
    13  	"github.com/MetalBlockchain/metalgo/utils/rpc"
    14  	"github.com/MetalBlockchain/metalgo/vms/example/xsvm/block"
    15  	"github.com/MetalBlockchain/metalgo/vms/example/xsvm/genesis"
    16  	"github.com/MetalBlockchain/metalgo/vms/example/xsvm/tx"
    17  	"github.com/MetalBlockchain/metalgo/vms/platformvm/warp"
    18  )
    19  
    20  const DefaultPollingInterval = 50 * time.Millisecond
    21  
    22  // Client defines the xsvm API client.
    23  type Client interface {
    24  	Network(
    25  		ctx context.Context,
    26  		options ...rpc.Option,
    27  	) (uint32, ids.ID, ids.ID, error)
    28  	Genesis(
    29  		ctx context.Context,
    30  		options ...rpc.Option,
    31  	) (*genesis.Genesis, error)
    32  	Nonce(
    33  		ctx context.Context,
    34  		address ids.ShortID,
    35  		options ...rpc.Option,
    36  	) (uint64, error)
    37  	Balance(
    38  		ctx context.Context,
    39  		address ids.ShortID,
    40  		assetID ids.ID,
    41  		options ...rpc.Option,
    42  	) (uint64, error)
    43  	Loan(
    44  		ctx context.Context,
    45  		chainID ids.ID,
    46  		options ...rpc.Option,
    47  	) (uint64, error)
    48  	IssueTx(
    49  		ctx context.Context,
    50  		tx *tx.Tx,
    51  		options ...rpc.Option,
    52  	) (ids.ID, error)
    53  	LastAccepted(
    54  		ctx context.Context,
    55  		options ...rpc.Option,
    56  	) (ids.ID, *block.Stateless, error)
    57  	Block(
    58  		ctx context.Context,
    59  		blkID ids.ID,
    60  		options ...rpc.Option,
    61  	) (*block.Stateless, error)
    62  	Message(
    63  		ctx context.Context,
    64  		txID ids.ID,
    65  		options ...rpc.Option,
    66  	) (*warp.UnsignedMessage, []byte, error)
    67  }
    68  
    69  func NewClient(uri, chain string) Client {
    70  	path := fmt.Sprintf(
    71  		"%s/ext/%s/%s",
    72  		uri,
    73  		constants.ChainAliasPrefix,
    74  		chain,
    75  	)
    76  	return &client{
    77  		req: rpc.NewEndpointRequester(path),
    78  	}
    79  }
    80  
    81  type client struct {
    82  	req rpc.EndpointRequester
    83  }
    84  
    85  func (c *client) Network(
    86  	ctx context.Context,
    87  	options ...rpc.Option,
    88  ) (uint32, ids.ID, ids.ID, error) {
    89  	resp := new(NetworkReply)
    90  	err := c.req.SendRequest(
    91  		ctx,
    92  		"xsvm.network",
    93  		nil,
    94  		resp,
    95  		options...,
    96  	)
    97  	return resp.NetworkID, resp.SubnetID, resp.ChainID, err
    98  }
    99  
   100  func (c *client) Genesis(
   101  	ctx context.Context,
   102  	options ...rpc.Option,
   103  ) (*genesis.Genesis, error) {
   104  	resp := new(GenesisReply)
   105  	err := c.req.SendRequest(
   106  		ctx,
   107  		"xsvm.genesis",
   108  		nil,
   109  		resp,
   110  		options...,
   111  	)
   112  	return resp.Genesis, err
   113  }
   114  
   115  func (c *client) Nonce(
   116  	ctx context.Context,
   117  	address ids.ShortID,
   118  	options ...rpc.Option,
   119  ) (uint64, error) {
   120  	resp := new(NonceReply)
   121  	err := c.req.SendRequest(
   122  		ctx,
   123  		"xsvm.nonce",
   124  		&NonceArgs{
   125  			Address: address,
   126  		},
   127  		resp,
   128  		options...,
   129  	)
   130  	return resp.Nonce, err
   131  }
   132  
   133  func (c *client) Balance(
   134  	ctx context.Context,
   135  	address ids.ShortID,
   136  	assetID ids.ID,
   137  	options ...rpc.Option,
   138  ) (uint64, error) {
   139  	resp := new(BalanceReply)
   140  	err := c.req.SendRequest(
   141  		ctx,
   142  		"xsvm.balance",
   143  		&BalanceArgs{
   144  			Address: address,
   145  			AssetID: assetID,
   146  		},
   147  		resp,
   148  		options...,
   149  	)
   150  	return resp.Balance, err
   151  }
   152  
   153  func (c *client) Loan(
   154  	ctx context.Context,
   155  	chainID ids.ID,
   156  	options ...rpc.Option,
   157  ) (uint64, error) {
   158  	resp := new(LoanReply)
   159  	err := c.req.SendRequest(
   160  		ctx,
   161  		"xsvm.loan",
   162  		&LoanArgs{
   163  			ChainID: chainID,
   164  		},
   165  		resp,
   166  		options...,
   167  	)
   168  	return resp.Amount, err
   169  }
   170  
   171  func (c *client) IssueTx(
   172  	ctx context.Context,
   173  	newTx *tx.Tx,
   174  	options ...rpc.Option,
   175  ) (ids.ID, error) {
   176  	txBytes, err := tx.Codec.Marshal(tx.CodecVersion, newTx)
   177  	if err != nil {
   178  		return ids.Empty, err
   179  	}
   180  
   181  	resp := new(IssueTxReply)
   182  	err = c.req.SendRequest(
   183  		ctx,
   184  		"xsvm.issueTx",
   185  		&IssueTxArgs{
   186  			Tx: txBytes,
   187  		},
   188  		resp,
   189  		options...,
   190  	)
   191  	return resp.TxID, err
   192  }
   193  
   194  func (c *client) LastAccepted(
   195  	ctx context.Context,
   196  	options ...rpc.Option,
   197  ) (ids.ID, *block.Stateless, error) {
   198  	resp := new(LastAcceptedReply)
   199  	err := c.req.SendRequest(
   200  		ctx,
   201  		"xsvm.lastAccepted",
   202  		nil,
   203  		resp,
   204  		options...,
   205  	)
   206  	return resp.BlockID, resp.Block, err
   207  }
   208  
   209  func (c *client) Block(
   210  	ctx context.Context,
   211  	blkID ids.ID,
   212  	options ...rpc.Option,
   213  ) (*block.Stateless, error) {
   214  	resp := new(BlockReply)
   215  	err := c.req.SendRequest(
   216  		ctx,
   217  		"xsvm.lastAccepted",
   218  		&BlockArgs{
   219  			BlockID: blkID,
   220  		},
   221  		resp,
   222  		options...,
   223  	)
   224  	return resp.Block, err
   225  }
   226  
   227  func (c *client) Message(
   228  	ctx context.Context,
   229  	txID ids.ID,
   230  	options ...rpc.Option,
   231  ) (*warp.UnsignedMessage, []byte, error) {
   232  	resp := new(MessageReply)
   233  	err := c.req.SendRequest(
   234  		ctx,
   235  		"xsvm.message",
   236  		&MessageArgs{
   237  			TxID: txID,
   238  		},
   239  		resp,
   240  		options...,
   241  	)
   242  	if err != nil {
   243  		return nil, nil, err
   244  	}
   245  	return resp.Message, resp.Signature, resp.Message.Initialize()
   246  }
   247  
   248  func AwaitTxAccepted(
   249  	ctx context.Context,
   250  	c Client,
   251  	address ids.ShortID,
   252  	nonce uint64,
   253  	freq time.Duration,
   254  	options ...rpc.Option,
   255  ) error {
   256  	ticker := time.NewTicker(freq)
   257  	defer ticker.Stop()
   258  
   259  	for {
   260  		currentNonce, err := c.Nonce(ctx, address, options...)
   261  		if err != nil {
   262  			return err
   263  		}
   264  
   265  		if currentNonce > nonce {
   266  			// The nonce increasing indicates the acceptance of a transaction
   267  			// issued with the specified nonce.
   268  			return nil
   269  		}
   270  
   271  		select {
   272  		case <-ctx.Done():
   273  			return ctx.Err()
   274  		case <-ticker.C:
   275  		}
   276  	}
   277  }