aboutsummaryrefslogtreecommitdiff
path: root/plugin/evm/service.go
diff options
context:
space:
mode:
Diffstat (limited to 'plugin/evm/service.go')
-rw-r--r--plugin/evm/service.go178
1 files changed, 176 insertions, 2 deletions
diff --git a/plugin/evm/service.go b/plugin/evm/service.go
index a934941..65ef3a2 100644
--- a/plugin/evm/service.go
+++ b/plugin/evm/service.go
@@ -19,17 +19,26 @@ import (
"github.com/ava-labs/avalanchego/utils/json"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
+ ethcrypto "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log"
)
const (
- version = "coreth-v0.3.7"
+ version = "coreth-v0.3.14"
)
// test constants
const (
GenesisTestAddr = "0x751a0b96e1042bee789452ecb20253fba40dbe85"
GenesisTestKey = "0xabd71b35d559563fea757f0f5edbde286fb8c043105b15abb7cd57189306d7d1"
+
+ // Max number of addresses that can be passed in as argument to GetUTXOs
+ maxGetUTXOsAddrs = 1024
+)
+
+var (
+ errNoAddresses = errors.New("no addresses provided")
+ errNoSourceChain = errors.New("no source chain provided")
)
// SnowmanAPI introduces snowman specific functionality to the evm
@@ -50,6 +59,15 @@ func (s *NetAPI) PeerCount() hexutil.Uint { return hexutil.Uint(0) } // TODO: re
// Version returns the current ethereum protocol version.
func (s *NetAPI) Version() string { return fmt.Sprintf("%d", s.vm.networkID) }
+// Web3API offers helper API methods
+type Web3API struct{}
+
+// ClientVersion returns the version of the vm running
+func (s *Web3API) ClientVersion() string { return version }
+
+// Sha3 returns the bytes returned by hashing [input] with Keccak256
+func (s *Web3API) Sha3(input hexutil.Bytes) hexutil.Bytes { return ethcrypto.Keccak256(input) }
+
// GetAcceptedFrontReply defines the reply that will be sent from the
// GetAcceptedFront API call
type GetAcceptedFrontReply struct {
@@ -244,7 +262,7 @@ type ExportArgs struct {
// It must be imported on the X-Chain to complete the transfer
func (service *AvaxAPI) Export(_ *http.Request, args *ExportArgs, response *api.JSONTxID) error {
log.Info("EVM: Export called")
- if args.AssetID.IsZero() {
+ if args.AssetID == ids.Empty {
return fmt.Errorf("assetID is required")
}
@@ -285,3 +303,159 @@ func (service *AvaxAPI) Export(_ *http.Request, args *ExportArgs, response *api.
response.TxID = tx.ID()
return service.vm.issueTx(tx)
}
+
+// Index is an address and an associated UTXO.
+// Marks a starting or stopping point when fetching UTXOs. Used for pagination.
+type Index struct {
+ Address string `json:"address"` // The address as a string
+ UTXO string `json:"utxo"` // The UTXO ID as a string
+}
+
+// GetUTXOsArgs are arguments for passing into GetUTXOs.
+// Gets the UTXOs that reference at least one address in [Addresses].
+// Returns at most [limit] addresses.
+// If specified, [SourceChain] is the chain where the atomic UTXOs were exported from. If not specified,
+// then GetUTXOs returns an error since the C Chain only has atomic UTXOs.
+// If [limit] == 0 or > [maxUTXOsToFetch], fetches up to [maxUTXOsToFetch].
+// [StartIndex] defines where to start fetching UTXOs (for pagination.)
+// UTXOs fetched are from addresses equal to or greater than [StartIndex.Address]
+// For address [StartIndex.Address], only UTXOs with IDs greater than [StartIndex.UTXO] will be returned.
+// If [StartIndex] is omitted, gets all UTXOs.
+// If GetUTXOs is called multiple times, with our without [StartIndex], it is not guaranteed
+// that returned UTXOs are unique. That is, the same UTXO may appear in the response of multiple calls.
+type GetUTXOsArgs struct {
+ Addresses []string `json:"addresses"`
+ SourceChain string `json:"sourceChain"`
+ Limit json.Uint32 `json:"limit"`
+ StartIndex Index `json:"startIndex"`
+ Encoding string `json:"encoding"`
+}
+
+// GetUTXOsReply defines the GetUTXOs replies returned from the API
+type GetUTXOsReply struct {
+ // Number of UTXOs returned
+ NumFetched json.Uint64 `json:"numFetched"`
+ // The UTXOs
+ UTXOs []string `json:"utxos"`
+ // The last UTXO that was returned, and the address it corresponds to.
+ // Used for pagination. To get the rest of the UTXOs, call GetUTXOs
+ // again and set [StartIndex] to this value.
+ EndIndex Index `json:"endIndex"`
+ // Encoding specifies the encoding format the UTXOs are returned in
+ Encoding string `json:"encoding"`
+}
+
+// GetUTXOs gets all utxos for passed in addresses
+func (service *AvaxAPI) GetUTXOs(r *http.Request, args *GetUTXOsArgs, reply *GetUTXOsReply) error {
+ service.vm.ctx.Log.Info("EVM: GetUTXOs called for with %s", args.Addresses)
+
+ if len(args.Addresses) == 0 {
+ return errNoAddresses
+ }
+ if len(args.Addresses) > maxGetUTXOsAddrs {
+ return fmt.Errorf("number of addresses given, %d, exceeds maximum, %d", len(args.Addresses), maxGetUTXOsAddrs)
+ }
+
+ encoding, err := service.vm.encodingManager.GetEncoding(args.Encoding)
+ if err != nil {
+ return fmt.Errorf("problem getting encoding formatter for '%s': %w", args.Encoding, err)
+ }
+
+ sourceChain := ids.ID{}
+ if args.SourceChain == "" {
+ return errNoSourceChain
+ }
+
+ chainID, err := service.vm.ctx.BCLookup.Lookup(args.SourceChain)
+ if err != nil {
+ return fmt.Errorf("problem parsing source chainID %q: %w", args.SourceChain, err)
+ }
+ sourceChain = chainID
+
+ addrSet := ids.ShortSet{}
+ for _, addrStr := range args.Addresses {
+ addr, err := service.vm.ParseLocalAddress(addrStr)
+ if err != nil {
+ return fmt.Errorf("couldn't parse address %q: %w", addrStr, err)
+ }
+ addrSet.Add(addr)
+ }
+
+ startAddr := ids.ShortEmpty
+ startUTXO := ids.Empty
+ if args.StartIndex.Address != "" || args.StartIndex.UTXO != "" {
+ startAddr, err = service.vm.ParseLocalAddress(args.StartIndex.Address)
+ if err != nil {
+ return fmt.Errorf("couldn't parse start index address %q: %w", args.StartIndex.Address, err)
+ }
+ startUTXO, err = ids.FromString(args.StartIndex.UTXO)
+ if err != nil {
+ return fmt.Errorf("couldn't parse start index utxo: %w", err)
+ }
+ }
+
+ utxos, endAddr, endUTXOID, err := service.vm.GetAtomicUTXOs(
+ sourceChain,
+ addrSet,
+ startAddr,
+ startUTXO,
+ int(args.Limit),
+ )
+ if err != nil {
+ return fmt.Errorf("problem retrieving UTXOs: %w", err)
+ }
+
+ reply.UTXOs = make([]string, len(utxos))
+ for i, utxo := range utxos {
+ b, err := service.vm.codec.Marshal(utxo)
+ if err != nil {
+ return fmt.Errorf("problem marshalling UTXO: %w", err)
+ }
+ reply.UTXOs[i] = encoding.ConvertBytes(b)
+ }
+
+ endAddress, err := service.vm.FormatLocalAddress(endAddr)
+ if err != nil {
+ return fmt.Errorf("problem formatting address: %w", err)
+ }
+
+ reply.EndIndex.Address = endAddress
+ reply.EndIndex.UTXO = endUTXOID.String()
+ reply.NumFetched = json.Uint64(len(utxos))
+ reply.Encoding = encoding.Encoding()
+ return nil
+}
+
+// IssueTx ...
+func (service *AvaxAPI) IssueTx(r *http.Request, args *api.FormattedTx, response *api.JSONTxID) error {
+ log.Info("EVM: IssueTx called")
+
+ encoding, err := service.vm.encodingManager.GetEncoding(args.Encoding)
+ if err != nil {
+ return fmt.Errorf("problem getting encoding formatter for '%s': %w", args.Encoding, err)
+ }
+ txBytes, err := encoding.ConvertString(args.Tx)
+ if err != nil {
+ return fmt.Errorf("problem decoding transaction: %w", err)
+ }
+
+ tx := &Tx{}
+ if err := service.vm.codec.Unmarshal(txBytes, tx); err != nil {
+ return fmt.Errorf("problem parsing transaction: %w", err)
+ }
+ if err := tx.Sign(service.vm.codec, nil); err != nil {
+ return fmt.Errorf("problem initializing transaction: %w", err)
+ }
+
+ utx, ok := tx.UnsignedTx.(UnsignedAtomicTx)
+ if !ok {
+ return errors.New("cannot issue non-atomic transaction through IssueTx API")
+ }
+
+ if err := utx.SemanticVerify(service.vm, tx); err != nil {
+ return err
+ }
+
+ response.TxID = tx.ID()
+ return service.vm.issueTx(tx)
+}