aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDeterminant <tederminant@gmail.com>2020-08-20 00:14:34 -0400
committerDeterminant <tederminant@gmail.com>2020-08-20 00:14:34 -0400
commit9f503c997bdb67a40ac2817c6cf0eb780a86f3c1 (patch)
treeb777bb52acc5d3d769be79821a5b775836d7af74
parent0c417aaa02c5cc8bb6a9629ac2502e0c4d200071 (diff)
add C-to-X RPC
-rw-r--r--plugin/evm/service.go60
-rw-r--r--plugin/evm/vm.go43
2 files changed, 92 insertions, 11 deletions
diff --git a/plugin/evm/service.go b/plugin/evm/service.go
index 3165ca7..29ef35d 100644
--- a/plugin/evm/service.go
+++ b/plugin/evm/service.go
@@ -6,6 +6,7 @@ package evm
import (
"context"
"crypto/rand"
+ "errors"
"fmt"
"math/big"
"net/http"
@@ -18,6 +19,7 @@ import (
"github.com/ava-labs/gecko/utils/constants"
"github.com/ava-labs/gecko/utils/crypto"
"github.com/ava-labs/gecko/utils/formatting"
+ "github.com/ava-labs/gecko/utils/json"
"github.com/ava-labs/go-ethereum/common"
"github.com/ava-labs/go-ethereum/common/hexutil"
ethcrypto "github.com/ava-labs/go-ethereum/crypto"
@@ -149,7 +151,7 @@ func (service *AvaAPI) ExportKey(r *http.Request, args *ExportKeyArgs, reply *Ex
return fmt.Errorf("problem retrieving user '%s': %w", args.Username, err)
}
user := user{db: db}
- if address, err := service.vm.ParseLocalAddress(args.Address); err != nil {
+ if address, err := service.vm.ParseEthAddress(args.Address); err != nil {
return fmt.Errorf("couldn't parse %s to address: %s", args.Address, err)
} else if sk, err := user.getKey(address); err != nil {
return fmt.Errorf("problem retrieving private key: %w", err)
@@ -201,7 +203,7 @@ func (service *AvaAPI) ImportKey(r *http.Request, args *ImportKeyArgs, reply *ap
}
// TODO: return eth address here
- reply.Address, err = service.vm.FormatAddress(GetEthAddress(sk))
+ reply.Address, err = service.vm.FormatEthAddress(GetEthAddress(sk))
if err != nil {
return fmt.Errorf("problem formatting address: %w", err)
}
@@ -236,7 +238,7 @@ func (service *AvaAPI) ImportAVAX(_ *http.Request, args *ImportAVAXArgs, respons
}
user := user{db: db}
- to, err := service.vm.ParseLocalAddress(args.To)
+ to, err := service.vm.ParseEthAddress(args.To)
if err != nil { // Parse address
return fmt.Errorf("couldn't parse argument 'to' to an address: %w", err)
}
@@ -254,3 +256,55 @@ func (service *AvaAPI) ImportAVAX(_ *http.Request, args *ImportAVAXArgs, respons
response.TxID = tx.ID()
return service.vm.issueTx(tx)
}
+
+// ExportAVAXArgs are the arguments to ExportAVAX
+type ExportAVAXArgs struct {
+ api.UserPass
+
+ // Amount of AVAX 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.
+ To string `json:"to"`
+}
+
+// ExportAVAX exports AVAX from the P-Chain to the X-Chain
+// It must be imported on the X-Chain to complete the transfer
+func (service *AvaAPI) ExportAVAX(_ *http.Request, args *ExportAVAXArgs, response *api.JsonTxID) error {
+ service.vm.ctx.Log.Info("Platform: ExportAVAX called")
+
+ if args.Amount == 0 {
+ return errors.New("argument 'amount' must be > 0")
+ }
+
+ chainID, to, err := service.vm.ParseAddress(args.To)
+ if err != nil {
+ return err
+ }
+
+ // Get this user's data
+ db, err := service.vm.ctx.Keystore.GetDatabase(args.Username, args.Password)
+ if err != nil {
+ return fmt.Errorf("problem retrieving user '%s': %w", args.Username, err)
+ }
+ user := user{db: db}
+ privKeys, err := user.getKeys()
+ if err != nil {
+ return fmt.Errorf("couldn't get addresses controlled by the user: %w", err)
+ }
+
+ // Create the transaction
+ tx, err := service.vm.newExportTx(
+ uint64(args.Amount), // Amount
+ chainID, // ID of the chain to send the funds to
+ to, // Address
+ privKeys, // Private keys
+ )
+ if err != nil {
+ return fmt.Errorf("couldn't create tx: %w", err)
+ }
+
+ response.TxID = tx.ID()
+ return service.vm.issueTx(tx)
+}
diff --git a/plugin/evm/vm.go b/plugin/evm/vm.go
index 6155728..f62bc7b 100644
--- a/plugin/evm/vm.go
+++ b/plugin/evm/vm.go
@@ -25,7 +25,7 @@ import (
ethcrypto "github.com/ava-labs/go-ethereum/crypto"
"github.com/ava-labs/go-ethereum/rlp"
"github.com/ava-labs/go-ethereum/rpc"
- avarpc "github.com/gorilla/rpc/v2"
+ geckorpc "github.com/gorilla/rpc/v2"
"github.com/ava-labs/gecko/api/admin"
"github.com/ava-labs/gecko/cache"
@@ -35,8 +35,10 @@ import (
"github.com/ava-labs/gecko/snow/choices"
"github.com/ava-labs/gecko/snow/consensus/snowman"
"github.com/ava-labs/gecko/utils/codec"
+ "github.com/ava-labs/gecko/utils/constants"
"github.com/ava-labs/gecko/utils/crypto"
- avajson "github.com/ava-labs/gecko/utils/json"
+ "github.com/ava-labs/gecko/utils/formatting"
+ geckojson "github.com/ava-labs/gecko/utils/json"
"github.com/ava-labs/gecko/utils/timer"
"github.com/ava-labs/gecko/utils/wrappers"
"github.com/ava-labs/gecko/vms/components/avax"
@@ -454,9 +456,9 @@ func (vm *VM) LastAccepted() ids.ID {
// By default the LockOption is WriteLock
// [lockOption] should have either 0 or 1 elements. Elements beside the first are ignored.
func newHandler(name string, service interface{}, lockOption ...commonEng.LockOption) *commonEng.HTTPHandler {
- server := avarpc.NewServer()
- server.RegisterCodec(avajson.NewCodec(), "application/json")
- server.RegisterCodec(avajson.NewCodec(), "application/json;charset=UTF-8")
+ server := geckorpc.NewServer()
+ server.RegisterCodec(geckojson.NewCodec(), "application/json")
+ server.RegisterCodec(geckojson.NewCodec(), "application/json;charset=UTF-8")
server.RegisterService(service, name)
var lock commonEng.LockOption = commonEng.WriteLock
@@ -643,18 +645,43 @@ func (vm *VM) getLastAccepted() *Block {
return vm.lastAccepted
}
-// ParseLocalAddress takes in an address for this chain and produces the ID
-func (vm *VM) ParseLocalAddress(addrStr string) (common.Address, error) {
+func (vm *VM) ParseEthAddress(addrStr string) (common.Address, error) {
if !common.IsHexAddress(addrStr) {
return common.Address{}, errInvalidAddr
}
return common.HexToAddress(addrStr), nil
}
-func (vm *VM) FormatAddress(addr common.Address) (string, error) {
+func (vm *VM) FormatEthAddress(addr common.Address) (string, error) {
return addr.Hex(), nil
}
+// ParseAddress takes in an address and produces the ID of the chain it's for
+// the ID of the address
+func (vm *VM) ParseAddress(addrStr string) (ids.ID, ids.ShortID, error) {
+ chainIDAlias, hrp, addrBytes, err := formatting.ParseAddress(addrStr)
+ if err != nil {
+ return ids.ID{}, ids.ShortID{}, err
+ }
+
+ chainID, err := vm.ctx.BCLookup.Lookup(chainIDAlias)
+ if err != nil {
+ return ids.ID{}, ids.ShortID{}, err
+ }
+
+ expectedHRP := constants.GetHRP(vm.ctx.NetworkID)
+ if hrp != expectedHRP {
+ return ids.ID{}, ids.ShortID{}, fmt.Errorf("expected hrp %q but got %q",
+ expectedHRP, hrp)
+ }
+
+ addr, err := ids.ToShortID(addrBytes)
+ if err != nil {
+ return ids.ID{}, ids.ShortID{}, err
+ }
+ return chainID, addr, nil
+}
+
func (vm *VM) issueTx(tx *Tx) error {
select {
case vm.pendingAtomicTxs <- tx: