github.com/dim4egster/coreth@v0.10.2/plugin/evm/client.go (about)

     1  // (c) 2019-2020, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package evm
     5  
     6  import (
     7  	"context"
     8  	"fmt"
     9  
    10  	"github.com/ethereum/go-ethereum/log"
    11  
    12  	"github.com/dim4egster/qmallgo/api"
    13  	"github.com/dim4egster/qmallgo/ids"
    14  	"github.com/dim4egster/qmallgo/utils/crypto"
    15  	"github.com/dim4egster/qmallgo/utils/formatting"
    16  	"github.com/dim4egster/qmallgo/utils/rpc"
    17  
    18  	cjson "github.com/dim4egster/qmallgo/utils/json"
    19  )
    20  
    21  // Interface compliance
    22  var _ Client = (*client)(nil)
    23  
    24  // Client interface for interacting with EVM [chain]
    25  type Client interface {
    26  	IssueTx(ctx context.Context, txBytes []byte) (ids.ID, error)
    27  	GetAtomicTxStatus(ctx context.Context, txID ids.ID) (Status, error)
    28  	GetAtomicTx(ctx context.Context, txID ids.ID) ([]byte, error)
    29  	GetAtomicUTXOs(ctx context.Context, addrs []string, sourceChain string, limit uint32, startAddress, startUTXOID string) ([][]byte, api.Index, error)
    30  	ListAddresses(ctx context.Context, userPass api.UserPass) ([]string, error)
    31  	ExportKey(ctx context.Context, userPass api.UserPass, addr string) (*crypto.PrivateKeySECP256K1R, string, error)
    32  	ImportKey(ctx context.Context, userPass api.UserPass, privateKey *crypto.PrivateKeySECP256K1R) (string, error)
    33  	Import(ctx context.Context, userPass api.UserPass, to string, sourceChain string) (ids.ID, error)
    34  	ExportAVAX(ctx context.Context, userPass api.UserPass, amount uint64, to string) (ids.ID, error)
    35  	Export(ctx context.Context, userPass api.UserPass, amount uint64, to string, assetID string) (ids.ID, error)
    36  	StartCPUProfiler(ctx context.Context) error
    37  	StopCPUProfiler(ctx context.Context) error
    38  	MemoryProfile(ctx context.Context) error
    39  	LockProfile(ctx context.Context) error
    40  	SetLogLevel(ctx context.Context, level log.Lvl) error
    41  	GetVMConfig(ctx context.Context) (*Config, error)
    42  }
    43  
    44  // Client implementation for interacting with EVM [chain]
    45  type client struct {
    46  	requester      rpc.EndpointRequester
    47  	adminRequester rpc.EndpointRequester
    48  }
    49  
    50  // NewClient returns a Client for interacting with EVM [chain]
    51  func NewClient(uri, chain string) Client {
    52  	return &client{
    53  		requester:      rpc.NewEndpointRequester(fmt.Sprintf("%s/ext/bc/%s/avax", uri, chain), "avax"),
    54  		adminRequester: rpc.NewEndpointRequester(fmt.Sprintf("%s/ext/bc/%s/admin", uri, chain), "admin"),
    55  	}
    56  }
    57  
    58  // NewCChainClient returns a Client for interacting with the C Chain
    59  func NewCChainClient(uri string) Client {
    60  	return NewClient(uri, "C")
    61  }
    62  
    63  // IssueTx issues a transaction to a node and returns the TxID
    64  func (c *client) IssueTx(ctx context.Context, txBytes []byte) (ids.ID, error) {
    65  	res := &api.JSONTxID{}
    66  	txStr, err := formatting.Encode(formatting.Hex, txBytes)
    67  	if err != nil {
    68  		return res.TxID, fmt.Errorf("problem hex encoding bytes: %w", err)
    69  	}
    70  	err = c.requester.SendRequest(ctx, "issueTx", &api.FormattedTx{
    71  		Tx:       txStr,
    72  		Encoding: formatting.Hex,
    73  	}, res)
    74  	return res.TxID, err
    75  }
    76  
    77  // GetAtomicTxStatus returns the status of [txID]
    78  func (c *client) GetAtomicTxStatus(ctx context.Context, txID ids.ID) (Status, error) {
    79  	res := &GetAtomicTxStatusReply{}
    80  	err := c.requester.SendRequest(ctx, "getAtomicTxStatus", &api.JSONTxID{
    81  		TxID: txID,
    82  	}, res)
    83  	return res.Status, err
    84  }
    85  
    86  // GetAtomicTx returns the byte representation of [txID]
    87  func (c *client) GetAtomicTx(ctx context.Context, txID ids.ID) ([]byte, error) {
    88  	res := &api.FormattedTx{}
    89  	err := c.requester.SendRequest(ctx, "getAtomicTx", &api.GetTxArgs{
    90  		TxID:     txID,
    91  		Encoding: formatting.Hex,
    92  	}, res)
    93  	if err != nil {
    94  		return nil, err
    95  	}
    96  
    97  	return formatting.Decode(formatting.Hex, res.Tx)
    98  }
    99  
   100  // GetAtomicUTXOs returns the byte representation of the atomic UTXOs controlled by [addresses]
   101  // from [sourceChain]
   102  func (c *client) GetAtomicUTXOs(ctx context.Context, addrs []string, sourceChain string, limit uint32, startAddress, startUTXOID string) ([][]byte, api.Index, error) {
   103  	res := &api.GetUTXOsReply{}
   104  	err := c.requester.SendRequest(ctx, "getUTXOs", &api.GetUTXOsArgs{
   105  		Addresses:   addrs,
   106  		SourceChain: sourceChain,
   107  		Limit:       cjson.Uint32(limit),
   108  		StartIndex: api.Index{
   109  			Address: startAddress,
   110  			UTXO:    startUTXOID,
   111  		},
   112  		Encoding: formatting.Hex,
   113  	}, res)
   114  	if err != nil {
   115  		return nil, api.Index{}, err
   116  	}
   117  
   118  	utxos := make([][]byte, len(res.UTXOs))
   119  	for i, utxo := range res.UTXOs {
   120  		b, err := formatting.Decode(formatting.Hex, utxo)
   121  		if err != nil {
   122  			return nil, api.Index{}, err
   123  		}
   124  		utxos[i] = b
   125  	}
   126  	return utxos, res.EndIndex, nil
   127  }
   128  
   129  // ListAddresses returns all addresses on this chain controlled by [user]
   130  func (c *client) ListAddresses(ctx context.Context, user api.UserPass) ([]string, error) {
   131  	res := &api.JSONAddresses{}
   132  	err := c.requester.SendRequest(ctx, "listAddresses", &user, res)
   133  	return res.Addresses, err
   134  }
   135  
   136  // ExportKey returns the private key corresponding to [addr] controlled by [user]
   137  // in both Avalanche standard format and hex format
   138  func (c *client) ExportKey(ctx context.Context, user api.UserPass, addr string) (*crypto.PrivateKeySECP256K1R, string, error) {
   139  	res := &ExportKeyReply{}
   140  	err := c.requester.SendRequest(ctx, "exportKey", &ExportKeyArgs{
   141  		UserPass: user,
   142  		Address:  addr,
   143  	}, res)
   144  	return res.PrivateKey, res.PrivateKeyHex, err
   145  }
   146  
   147  // ImportKey imports [privateKey] to [user]
   148  func (c *client) ImportKey(ctx context.Context, user api.UserPass, privateKey *crypto.PrivateKeySECP256K1R) (string, error) {
   149  	res := &api.JSONAddress{}
   150  	err := c.requester.SendRequest(ctx, "importKey", &ImportKeyArgs{
   151  		UserPass:   user,
   152  		PrivateKey: privateKey,
   153  	}, res)
   154  	return res.Address, err
   155  }
   156  
   157  // Import sends an import transaction to import funds from [sourceChain] and
   158  // returns the ID of the newly created transaction
   159  func (c *client) Import(ctx context.Context, user api.UserPass, to, sourceChain string) (ids.ID, error) {
   160  	res := &api.JSONTxID{}
   161  	err := c.requester.SendRequest(ctx, "import", &ImportArgs{
   162  		UserPass:    user,
   163  		To:          to,
   164  		SourceChain: sourceChain,
   165  	}, res)
   166  	return res.TxID, err
   167  }
   168  
   169  // ExportAVAX sends AVAX from this chain to the address specified by [to].
   170  // Returns the ID of the newly created atomic transaction
   171  func (c *client) ExportAVAX(
   172  	ctx context.Context,
   173  	user api.UserPass,
   174  	amount uint64,
   175  	to string,
   176  ) (ids.ID, error) {
   177  	return c.Export(ctx, user, amount, to, "AVAX")
   178  }
   179  
   180  // Export sends an asset from this chain to the P/C-Chain.
   181  // After this tx is accepted, the AVAX must be imported to the P/C-chain with an importTx.
   182  // Returns the ID of the newly created atomic transaction
   183  func (c *client) Export(
   184  	ctx context.Context,
   185  	user api.UserPass,
   186  	amount uint64,
   187  	to string,
   188  	assetID string,
   189  ) (ids.ID, error) {
   190  	res := &api.JSONTxID{}
   191  	err := c.requester.SendRequest(ctx, "export", &ExportArgs{
   192  		ExportAVAXArgs: ExportAVAXArgs{
   193  			UserPass: user,
   194  			Amount:   cjson.Uint64(amount),
   195  			To:       to,
   196  		},
   197  		AssetID: assetID,
   198  	}, res)
   199  	return res.TxID, err
   200  }
   201  
   202  func (c *client) StartCPUProfiler(ctx context.Context) error {
   203  	return c.adminRequester.SendRequest(ctx, "startCPUProfiler", struct{}{}, &api.EmptyReply{})
   204  }
   205  
   206  func (c *client) StopCPUProfiler(ctx context.Context) error {
   207  	return c.adminRequester.SendRequest(ctx, "stopCPUProfiler", struct{}{}, &api.EmptyReply{})
   208  }
   209  
   210  func (c *client) MemoryProfile(ctx context.Context) error {
   211  	return c.adminRequester.SendRequest(ctx, "memoryProfile", struct{}{}, &api.EmptyReply{})
   212  }
   213  
   214  func (c *client) LockProfile(ctx context.Context) error {
   215  	return c.adminRequester.SendRequest(ctx, "lockProfile", struct{}{}, &api.EmptyReply{})
   216  }
   217  
   218  // SetLogLevel dynamically sets the log level for the C Chain
   219  func (c *client) SetLogLevel(ctx context.Context, level log.Lvl) error {
   220  	return c.adminRequester.SendRequest(ctx, "setLogLevel", &SetLogLevelArgs{
   221  		Level: level.String(),
   222  	}, &api.EmptyReply{})
   223  }
   224  
   225  // GetVMConfig returns the current config of the VM
   226  func (c *client) GetVMConfig(ctx context.Context) (*Config, error) {
   227  	res := &ConfigReply{}
   228  	err := c.adminRequester.SendRequest(ctx, "getVMConfig", struct{}{}, res)
   229  	return res.Config, err
   230  }