Files
coreth/plugin/evm/client/client.go
zeekay f70bac500d refactor: migrate to vm/rpc/chain, remove node dependency from production code
- Update import from vm/rpcchainvm to vm/rpc/chain
- Update vm dependency to v1.0.18
- node dependency now test-only (platformvm.NewClient for warp tests)
2026-01-11 21:38:33 -08:00

175 lines
6.2 KiB
Go

// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package client
import (
"context"
"errors"
"fmt"
"log/slog"
"github.com/luxfi/formatting"
"github.com/luxfi/address"
"github.com/luxfi/codec/jsonrpc"
"github.com/luxfi/coreth/plugin/evm/atomic"
"github.com/luxfi/coreth/plugin/evm/config"
"github.com/luxfi/ids"
"github.com/luxfi/rpc"
"github.com/luxfi/vm/api"
)
// Interface compliance
var _ Client = (*client)(nil)
var errInvalidAddr = errors.New("invalid hex address")
// Client interface for interacting with EVM [chain]
type Client interface {
IssueTx(ctx context.Context, txBytes []byte, options ...rpc.Option) (ids.ID, error)
GetAtomicTxStatus(ctx context.Context, txID ids.ID, options ...rpc.Option) (atomic.Status, error)
GetAtomicTx(ctx context.Context, txID ids.ID, options ...rpc.Option) ([]byte, error)
GetAtomicUTXOs(ctx context.Context, addrs []ids.ShortID, sourceChain string, limit uint32, startAddress ids.ShortID, startUTXOID ids.ID, options ...rpc.Option) ([][]byte, ids.ShortID, ids.ID, error)
StartCPUProfiler(ctx context.Context, options ...rpc.Option) error
StopCPUProfiler(ctx context.Context, options ...rpc.Option) error
MemoryProfile(ctx context.Context, options ...rpc.Option) error
LockProfile(ctx context.Context, options ...rpc.Option) error
SetLogLevel(ctx context.Context, level slog.Level, options ...rpc.Option) error
GetVMConfig(ctx context.Context, options ...rpc.Option) (*config.Config, error)
}
// Client implementation for interacting with EVM [chain]
type client struct {
requester rpc.EndpointRequester
adminRequester rpc.EndpointRequester
}
// NewClient returns a Client for interacting with EVM [chain]
func NewClient(uri, chain string) Client {
return &client{
requester: rpc.NewEndpointRequester(fmt.Sprintf("%s/ext/bc/%s/lux", uri, chain)),
adminRequester: rpc.NewEndpointRequester(fmt.Sprintf("%s/ext/bc/%s/admin", uri, chain)),
}
}
// NewCChainClient returns a Client for interacting with the C Chain
func NewCChainClient(uri string) Client {
return NewClient(uri, "C")
}
// IssueTx issues a transaction to a node and returns the TxID
func (c *client) IssueTx(ctx context.Context, txBytes []byte, options ...rpc.Option) (ids.ID, error) {
res := &api.JSONTxID{}
txStr, err := formatting.Encode(formatting.Hex, txBytes)
if err != nil {
return res.TxID, fmt.Errorf("problem hex encoding bytes: %w", err)
}
err = c.requester.SendRequest(ctx, "lux.issueTx", &api.FormattedTx{
Tx: txStr,
Encoding: formatting.Hex,
}, res, options...)
return res.TxID, err
}
// GetAtomicTxStatusReply defines the GetAtomicTxStatus replies returned from the API
type GetAtomicTxStatusReply struct {
Status atomic.Status `json:"status"`
BlockHeight *json.Uint64 `json:"blockHeight,omitempty"`
}
// GetAtomicTxStatus returns the status of [txID]
func (c *client) GetAtomicTxStatus(ctx context.Context, txID ids.ID, options ...rpc.Option) (atomic.Status, error) {
res := &GetAtomicTxStatusReply{}
err := c.requester.SendRequest(ctx, "lux.getAtomicTxStatus", &api.JSONTxID{
TxID: txID,
}, res, options...)
return res.Status, err
}
// GetAtomicTx returns the byte representation of [txID]
func (c *client) GetAtomicTx(ctx context.Context, txID ids.ID, options ...rpc.Option) ([]byte, error) {
res := &api.FormattedTx{}
err := c.requester.SendRequest(ctx, "lux.getAtomicTx", &api.GetTxArgs{
TxID: txID,
Encoding: formatting.Hex,
}, res, options...)
if err != nil {
return nil, err
}
return formatting.Decode(formatting.Hex, res.Tx)
}
// GetAtomicUTXOs returns the byte representation of the atomic UTXOs controlled by [addresses]
// from [sourceChain]
func (c *client) GetAtomicUTXOs(ctx context.Context, addrs []ids.ShortID, sourceChain string, limit uint32, startAddress ids.ShortID, startUTXOID ids.ID, options ...rpc.Option) ([][]byte, ids.ShortID, ids.ID, error) {
res := &api.GetUTXOsReply{}
err := c.requester.SendRequest(ctx, "lux.getUTXOs", &api.GetUTXOsArgs{
Addresses: ids.ShortIDsToStrings(addrs),
SourceChain: sourceChain,
Limit: json.Uint32(limit),
StartIndex: api.Index{
Address: startAddress.String(),
UTXO: startUTXOID.String(),
},
Encoding: formatting.Hex,
}, res, options...)
if err != nil {
return nil, ids.ShortID{}, ids.Empty, err
}
utxos := make([][]byte, len(res.UTXOs))
for i, utxo := range res.UTXOs {
utxoBytes, err := formatting.Decode(res.Encoding, utxo)
if err != nil {
return nil, ids.ShortID{}, ids.Empty, err
}
utxos[i] = utxoBytes
}
endAddr, err := address.ParseToID(res.EndIndex.Address)
if err != nil {
return nil, ids.ShortID{}, ids.Empty, err
}
endUTXOID, err := ids.FromString(res.EndIndex.UTXO)
return utxos, endAddr, endUTXOID, err
}
func (c *client) StartCPUProfiler(ctx context.Context, options ...rpc.Option) error {
return c.adminRequester.SendRequest(ctx, "admin.startCPUProfiler", struct{}{}, &api.EmptyReply{}, options...)
}
func (c *client) StopCPUProfiler(ctx context.Context, options ...rpc.Option) error {
return c.adminRequester.SendRequest(ctx, "admin.stopCPUProfiler", struct{}{}, &api.EmptyReply{}, options...)
}
func (c *client) MemoryProfile(ctx context.Context, options ...rpc.Option) error {
return c.adminRequester.SendRequest(ctx, "admin.memoryProfile", struct{}{}, &api.EmptyReply{}, options...)
}
func (c *client) LockProfile(ctx context.Context, options ...rpc.Option) error {
return c.adminRequester.SendRequest(ctx, "admin.lockProfile", struct{}{}, &api.EmptyReply{}, options...)
}
type SetLogLevelArgs struct {
Level string `json:"level"`
}
// SetLogLevel dynamically sets the log level for the C Chain
func (c *client) SetLogLevel(ctx context.Context, level slog.Level, options ...rpc.Option) error {
return c.adminRequester.SendRequest(ctx, "admin.setLogLevel", &SetLogLevelArgs{
Level: level.String(),
}, &api.EmptyReply{}, options...)
}
type ConfigReply struct {
Config *config.Config `json:"config"`
}
// GetVMConfig returns the current config of the VM
func (c *client) GetVMConfig(ctx context.Context, options ...rpc.Option) (*config.Config, error) {
res := &ConfigReply{}
err := c.adminRequester.SendRequest(ctx, "admin.getVMConfig", struct{}{}, res, options...)
return res.Config, err
}