-
Notifications
You must be signed in to change notification settings - Fork 160
Update AVAX client implementation and interface to align types #301
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,38 +7,38 @@ import ( | |
| "context" | ||
| "fmt" | ||
|
|
||
| "github.com/ethereum/go-ethereum/common" | ||
| "github.com/ethereum/go-ethereum/log" | ||
|
|
||
| "github.com/ava-labs/avalanchego/api" | ||
| "github.com/ava-labs/avalanchego/ids" | ||
| "github.com/ava-labs/avalanchego/utils/crypto/secp256k1" | ||
| "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" | ||
|
|
||
| cjson "github.com/ava-labs/avalanchego/utils/json" | ||
| ) | ||
|
|
||
| // Interface compliance | ||
| var _ Client = (*client)(nil) | ||
|
|
||
| // Client interface for interacting with EVM [chain] | ||
| type Client interface { | ||
| IssueTx(ctx context.Context, txBytes []byte) (ids.ID, error) | ||
| GetAtomicTxStatus(ctx context.Context, txID ids.ID) (Status, error) | ||
| GetAtomicTx(ctx context.Context, txID ids.ID) ([]byte, error) | ||
| GetAtomicUTXOs(ctx context.Context, addrs []string, sourceChain string, limit uint32, startAddress, startUTXOID string) ([][]byte, api.Index, error) | ||
| ListAddresses(ctx context.Context, userPass api.UserPass) ([]string, error) | ||
| ExportKey(ctx context.Context, userPass api.UserPass, addr string) (*secp256k1.PrivateKey, string, error) | ||
| ImportKey(ctx context.Context, userPass api.UserPass, privateKey *secp256k1.PrivateKey) (string, error) | ||
| Import(ctx context.Context, userPass api.UserPass, to string, sourceChain string) (ids.ID, error) | ||
| ExportAVAX(ctx context.Context, userPass api.UserPass, amount uint64, to string) (ids.ID, error) | ||
| Export(ctx context.Context, userPass api.UserPass, amount uint64, to string, assetID string) (ids.ID, error) | ||
| StartCPUProfiler(ctx context.Context) error | ||
| StopCPUProfiler(ctx context.Context) error | ||
| MemoryProfile(ctx context.Context) error | ||
| LockProfile(ctx context.Context) error | ||
| SetLogLevel(ctx context.Context, level log.Lvl) error | ||
| GetVMConfig(ctx context.Context) (*Config, error) | ||
| IssueTx(ctx context.Context, txBytes []byte, options ...rpc.Option) (ids.ID, error) | ||
| GetAtomicTxStatus(ctx context.Context, txID ids.ID, options ...rpc.Option) (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) | ||
| ExportKey(ctx context.Context, userPass api.UserPass, addr common.Address, options ...rpc.Option) (*secp256k1.PrivateKey, string, error) | ||
| ImportKey(ctx context.Context, userPass api.UserPass, privateKey *secp256k1.PrivateKey, options ...rpc.Option) (common.Address, error) | ||
| Import(ctx context.Context, userPass api.UserPass, to common.Address, sourceChain string, options ...rpc.Option) (ids.ID, error) | ||
| ExportAVAX(ctx context.Context, userPass api.UserPass, amount uint64, to ids.ShortID, targetChain string, options ...rpc.Option) (ids.ID, error) | ||
| Export(ctx context.Context, userPass api.UserPass, amount uint64, to ids.ShortID, targetChain string, assetID string, options ...rpc.Option) (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 log.Lvl, options ...rpc.Option) error | ||
| GetVMConfig(ctx context.Context, options ...rpc.Option) (*Config, error) | ||
| } | ||
|
|
||
| // Client implementation for interacting with EVM [chain] | ||
|
|
@@ -61,7 +61,7 @@ func NewCChainClient(uri string) Client { | |
| } | ||
|
|
||
| // IssueTx issues a transaction to a node and returns the TxID | ||
| func (c *client) IssueTx(ctx context.Context, txBytes []byte) (ids.ID, error) { | ||
| 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 { | ||
|
|
@@ -70,26 +70,26 @@ func (c *client) IssueTx(ctx context.Context, txBytes []byte) (ids.ID, error) { | |
| err = c.requester.SendRequest(ctx, "avax.issueTx", &api.FormattedTx{ | ||
| Tx: txStr, | ||
| Encoding: formatting.Hex, | ||
| }, res) | ||
| }, res, options...) | ||
| return res.TxID, err | ||
| } | ||
|
|
||
| // GetAtomicTxStatus returns the status of [txID] | ||
| func (c *client) GetAtomicTxStatus(ctx context.Context, txID ids.ID) (Status, error) { | ||
| func (c *client) GetAtomicTxStatus(ctx context.Context, txID ids.ID, options ...rpc.Option) (Status, error) { | ||
| res := &GetAtomicTxStatusReply{} | ||
| err := c.requester.SendRequest(ctx, "avax.getAtomicTxStatus", &api.JSONTxID{ | ||
| TxID: txID, | ||
| }, res) | ||
| }, res, options...) | ||
| return res.Status, err | ||
| } | ||
|
|
||
| // GetAtomicTx returns the byte representation of [txID] | ||
| func (c *client) GetAtomicTx(ctx context.Context, txID ids.ID) ([]byte, error) { | ||
| func (c *client) GetAtomicTx(ctx context.Context, txID ids.ID, options ...rpc.Option) ([]byte, error) { | ||
| res := &api.FormattedTx{} | ||
| err := c.requester.SendRequest(ctx, "avax.getAtomicTx", &api.GetTxArgs{ | ||
| TxID: txID, | ||
| Encoding: formatting.Hex, | ||
| }, res) | ||
| }, res, options...) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
@@ -99,70 +99,71 @@ func (c *client) GetAtomicTx(ctx context.Context, txID ids.ID) ([]byte, error) { | |
|
|
||
| // GetAtomicUTXOs returns the byte representation of the atomic UTXOs controlled by [addresses] | ||
| // from [sourceChain] | ||
| func (c *client) GetAtomicUTXOs(ctx context.Context, addrs []string, sourceChain string, limit uint32, startAddress, startUTXOID string) ([][]byte, api.Index, error) { | ||
| 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, "avax.getUTXOs", &api.GetUTXOsArgs{ | ||
| Addresses: addrs, | ||
| Addresses: ids.ShortIDsToStrings(addrs), | ||
| SourceChain: sourceChain, | ||
| Limit: cjson.Uint32(limit), | ||
| Limit: json.Uint32(limit), | ||
| StartIndex: api.Index{ | ||
| Address: startAddress, | ||
| UTXO: startUTXOID, | ||
| Address: startAddress.String(), | ||
| UTXO: startUTXOID.String(), | ||
| }, | ||
| Encoding: formatting.Hex, | ||
| }, res) | ||
| }, res, options...) | ||
| if err != nil { | ||
| return nil, api.Index{}, err | ||
| return nil, ids.ShortID{}, ids.Empty, err | ||
| } | ||
|
|
||
| utxos := make([][]byte, len(res.UTXOs)) | ||
| for i, utxo := range res.UTXOs { | ||
| b, err := formatting.Decode(formatting.Hex, utxo) | ||
| utxoBytes, err := formatting.Decode(res.Encoding, utxo) | ||
| if err != nil { | ||
| return nil, api.Index{}, err | ||
| return nil, ids.ShortID{}, ids.Empty, err | ||
| } | ||
| utxos[i] = b | ||
| utxos[i] = utxoBytes | ||
| } | ||
| return utxos, res.EndIndex, nil | ||
| } | ||
|
|
||
| // ListAddresses returns all addresses on this chain controlled by [user] | ||
| func (c *client) ListAddresses(ctx context.Context, user api.UserPass) ([]string, error) { | ||
| res := &api.JSONAddresses{} | ||
| err := c.requester.SendRequest(ctx, "avax.listAddresses", &user, res) | ||
| return res.Addresses, err | ||
| 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 | ||
| } | ||
|
|
||
| // ExportKey returns the private key corresponding to [addr] controlled by [user] | ||
| // in both Avalanche standard format and hex format | ||
| func (c *client) ExportKey(ctx context.Context, user api.UserPass, addr string) (*secp256k1.PrivateKey, string, error) { | ||
| func (c *client) ExportKey(ctx context.Context, user api.UserPass, addr common.Address, options ...rpc.Option) (*secp256k1.PrivateKey, string, error) { | ||
| res := &ExportKeyReply{} | ||
| err := c.requester.SendRequest(ctx, "avax.exportKey", &ExportKeyArgs{ | ||
| UserPass: user, | ||
| Address: addr, | ||
| }, res) | ||
| Address: addr.Hex(), | ||
| }, res, options...) | ||
| return res.PrivateKey, res.PrivateKeyHex, err | ||
| } | ||
|
|
||
| // ImportKey imports [privateKey] to [user] | ||
| func (c *client) ImportKey(ctx context.Context, user api.UserPass, privateKey *secp256k1.PrivateKey) (string, error) { | ||
| func (c *client) ImportKey(ctx context.Context, user api.UserPass, privateKey *secp256k1.PrivateKey, options ...rpc.Option) (common.Address, error) { | ||
| res := &api.JSONAddress{} | ||
| err := c.requester.SendRequest(ctx, "avax.importKey", &ImportKeyArgs{ | ||
| UserPass: user, | ||
| PrivateKey: privateKey, | ||
| }, res) | ||
| return res.Address, err | ||
| }, res, options...) | ||
| if err != nil { | ||
| return common.Address{}, err | ||
| } | ||
| return ParseEthAddress(res.Address) | ||
| } | ||
|
|
||
| // Import sends an import transaction to import funds from [sourceChain] and | ||
| // returns the ID of the newly created transaction | ||
| func (c *client) Import(ctx context.Context, user api.UserPass, to, sourceChain string) (ids.ID, error) { | ||
| func (c *client) Import(ctx context.Context, user api.UserPass, to common.Address, sourceChain string, options ...rpc.Option) (ids.ID, error) { | ||
| res := &api.JSONTxID{} | ||
| err := c.requester.SendRequest(ctx, "avax.import", &ImportArgs{ | ||
| UserPass: user, | ||
| To: to, | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (No action required) Why is it necessary to convert the address to hex for
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We could update the |
||
| SourceChain: sourceChain, | ||
| }, res) | ||
| }, res, options...) | ||
| return res.TxID, err | ||
| } | ||
|
|
||
|
|
@@ -172,9 +173,11 @@ func (c *client) ExportAVAX( | |
| ctx context.Context, | ||
| user api.UserPass, | ||
| amount uint64, | ||
| to string, | ||
| to ids.ShortID, | ||
| targetChain string, | ||
| options ...rpc.Option, | ||
| ) (ids.ID, error) { | ||
| return c.Export(ctx, user, amount, to, "AVAX") | ||
| return c.Export(ctx, user, amount, to, targetChain, "AVAX", options...) | ||
| } | ||
|
|
||
| // Export sends an asset from this chain to the P/C-Chain. | ||
|
|
@@ -184,47 +187,50 @@ func (c *client) Export( | |
| ctx context.Context, | ||
| user api.UserPass, | ||
| amount uint64, | ||
| to string, | ||
| to ids.ShortID, | ||
| targetChain string, | ||
| assetID string, | ||
| options ...rpc.Option, | ||
| ) (ids.ID, error) { | ||
| res := &api.JSONTxID{} | ||
| err := c.requester.SendRequest(ctx, "avax.export", &ExportArgs{ | ||
| ExportAVAXArgs: ExportAVAXArgs{ | ||
| UserPass: user, | ||
| Amount: cjson.Uint64(amount), | ||
| To: to, | ||
| UserPass: user, | ||
| Amount: json.Uint64(amount), | ||
| TargetChain: targetChain, | ||
| To: to.String(), | ||
| }, | ||
| AssetID: assetID, | ||
| }, res) | ||
| }, res, options...) | ||
| return res.TxID, err | ||
| } | ||
|
|
||
| func (c *client) StartCPUProfiler(ctx context.Context) error { | ||
| return c.adminRequester.SendRequest(ctx, "admin.startCPUProfiler", struct{}{}, &api.EmptyReply{}) | ||
| 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) error { | ||
| return c.adminRequester.SendRequest(ctx, "admin.stopCPUProfiler", struct{}{}, &api.EmptyReply{}) | ||
| 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) error { | ||
| return c.adminRequester.SendRequest(ctx, "admin.memoryProfile", struct{}{}, &api.EmptyReply{}) | ||
| 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) error { | ||
| return c.adminRequester.SendRequest(ctx, "admin.lockProfile", struct{}{}, &api.EmptyReply{}) | ||
| func (c *client) LockProfile(ctx context.Context, options ...rpc.Option) error { | ||
| return c.adminRequester.SendRequest(ctx, "admin.lockProfile", struct{}{}, &api.EmptyReply{}, options...) | ||
| } | ||
|
|
||
| // SetLogLevel dynamically sets the log level for the C Chain | ||
| func (c *client) SetLogLevel(ctx context.Context, level log.Lvl) error { | ||
| func (c *client) SetLogLevel(ctx context.Context, level log.Lvl, options ...rpc.Option) error { | ||
| return c.adminRequester.SendRequest(ctx, "admin.setLogLevel", &SetLogLevelArgs{ | ||
| Level: level.String(), | ||
| }, &api.EmptyReply{}) | ||
| }, &api.EmptyReply{}, options...) | ||
| } | ||
|
|
||
| // GetVMConfig returns the current config of the VM | ||
| func (c *client) GetVMConfig(ctx context.Context) (*Config, error) { | ||
| func (c *client) GetVMConfig(ctx context.Context, options ...rpc.Option) (*Config, error) { | ||
| res := &ConfigReply{} | ||
| err := c.adminRequester.SendRequest(ctx, "admin.getVMConfig", struct{}{}, res) | ||
| err := c.adminRequester.SendRequest(ctx, "admin.getVMConfig", struct{}{}, res, options...) | ||
| return res.Config, err | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -174,7 +174,7 @@ type ImportArgs struct { | |
| SourceChain string `json:"sourceChain"` | ||
|
|
||
| // The address that will receive the imported funds | ||
| To string `json:"to"` | ||
| To common.Address `json:"to"` | ||
| } | ||
|
|
||
| // ImportAVAX is a deprecated name for Import. | ||
|
|
@@ -192,11 +192,6 @@ func (service *AvaxAPI) Import(_ *http.Request, args *ImportArgs, response *api. | |
| return fmt.Errorf("problem parsing chainID %q: %w", args.SourceChain, err) | ||
| } | ||
|
|
||
| to, err := ParseEthAddress(args.To) | ||
| if err != nil { // Parse address | ||
| return fmt.Errorf("couldn't parse argument 'to' to an address: %w", err) | ||
| } | ||
|
|
||
| // Get the user's info | ||
| db, err := service.vm.ctx.Keystore.GetDatabase(args.Username, args.Password) | ||
| if err != nil { | ||
|
|
@@ -224,7 +219,7 @@ func (service *AvaxAPI) Import(_ *http.Request, args *ImportArgs, response *api. | |
| baseFee = args.BaseFee.ToInt() | ||
| } | ||
|
|
||
| tx, err := service.vm.newImportTx(chainID, to, baseFee, privKeys) | ||
| tx, err := service.vm.newImportTx(chainID, args.To, baseFee, privKeys) | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
@@ -243,8 +238,12 @@ type ExportAVAXArgs struct { | |
| // Amount of asset to send | ||
| Amount json.Uint64 `json:"amount"` | ||
|
|
||
| // ID of the address that will receive the AVAX. This address includes the | ||
| // chainID, which is used to determine what the destination chain is. | ||
| // Chain the funds are going to. Optional. Used if To address does not | ||
| // include the chainID. | ||
| TargetChain string `json:"targetChain"` | ||
|
|
||
| // ID of the address that will receive the AVAX. This address may include | ||
| // the chainID, which is used to determine what the destination chain is. | ||
| To string `json:"to"` | ||
| } | ||
|
|
||
|
|
@@ -278,9 +277,17 @@ func (service *AvaxAPI) Export(_ *http.Request, args *ExportArgs, response *api. | |
| return errors.New("argument 'amount' must be > 0") | ||
| } | ||
|
|
||
| // Get the chainID and parse the to address | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (No action required) Would it make sense to conditionally use the alternate parsing strategy based on the returned error? Or maybe the use of a full address could be deprecated and eventually removed? I'm not sure why its desirable to support the provision of the chain id in both ways vs picking one or the other. |
||
| chainID, to, err := service.vm.ParseAddress(args.To) | ||
| if err != nil { | ||
| return err | ||
| chainID, err = service.vm.ctx.BCLookup.Lookup(args.TargetChain) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| to, err = ids.ShortFromString(args.To) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| } | ||
|
|
||
| // Get this user's data | ||
|
|
@@ -350,7 +357,7 @@ func (service *AvaxAPI) GetUTXOs(r *http.Request, args *api.GetUTXOsArgs, reply | |
|
|
||
| addrSet := set.Set[ids.ShortID]{} | ||
| for _, addrStr := range args.Addresses { | ||
| addr, err := service.vm.ParseLocalAddress(addrStr) | ||
| addr, err := service.vm.ParseServiceAddress(addrStr) | ||
| if err != nil { | ||
| return fmt.Errorf("couldn't parse address %q: %w", addrStr, err) | ||
| } | ||
|
|
@@ -360,7 +367,7 @@ func (service *AvaxAPI) GetUTXOs(r *http.Request, args *api.GetUTXOsArgs, reply | |
| startAddr := ids.ShortEmpty | ||
| startUTXO := ids.Empty | ||
| if args.StartIndex.Address != "" || args.StartIndex.UTXO != "" { | ||
| startAddr, err = service.vm.ParseLocalAddress(args.StartIndex.Address) | ||
| startAddr, err = service.vm.ParseServiceAddress(args.StartIndex.Address) | ||
| if err != nil { | ||
| return fmt.Errorf("couldn't parse start index address %q: %w", args.StartIndex.Address, err) | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(No action required) It may be worth separating non-functional (e.g. naming) from functional changes when refactoring to simplify review.