From 88cc3698b3663972cd9b60faf5c14a7e1bbee965 Mon Sep 17 00:00:00 2001 From: Determinant Date: Thu, 13 Aug 2020 21:11:56 -0400 Subject: WIP: X-to-C transfer --- plugin/evm/service.go | 109 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) (limited to 'plugin/evm/service.go') diff --git a/plugin/evm/service.go b/plugin/evm/service.go index 62b124f..75e7c31 100644 --- a/plugin/evm/service.go +++ b/plugin/evm/service.go @@ -8,10 +8,14 @@ import ( "crypto/rand" "fmt" "math/big" + "net/http" + "strings" "github.com/ava-labs/coreth" "github.com/ava-labs/coreth/core/types" + "github.com/ava-labs/gecko/api" + "github.com/ava-labs/gecko/utils/constants" "github.com/ava-labs/go-ethereum/common" "github.com/ava-labs/go-ethereum/common/hexutil" "github.com/ava-labs/go-ethereum/crypto" @@ -36,6 +40,8 @@ type SnowmanAPI struct{ vm *VM } // NetAPI offers network related API methods type NetAPI struct{ vm *VM } +type AvaAPI struct{ vm *VM } + // NewNetAPI creates a new net API instance. func NewNetAPI(vm *VM) *NetAPI { return &NetAPI{vm} } @@ -120,3 +126,106 @@ func (api *DebugAPI) IssueBlock(ctx context.Context) error { return api.vm.tryBlockGen() } + +// ExportKeyArgs are arguments for ExportKey +type ExportKeyArgs struct { + api.UserPass + Address string `json:"address"` +} + +// ExportKeyReply is the response for ExportKey +type ExportKeyReply struct { + // The decrypted PrivateKey for the Address provided in the arguments + PrivateKey string `json:"privateKey"` +} + +// ExportKey returns a private key from the provided user +func (service *AvaAPI) ExportKey(r *http.Request, args *ExportKeyArgs, reply *ExportKeyReply) error { + service.vm.ctx.Log.Info("Platform: ExportKey called") + 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} + if address, err := service.vm.ParseAddress(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) + } else { + reply.PrivateKey = common.ToHex(crypto.FromECDSA(sk)) + //constants.SecretKeyPrefix + formatting.CB58{Bytes: sk.Bytes()}.String() + return nil + } +} + +// ImportKeyArgs are arguments for ImportKey +type ImportKeyArgs struct { + api.UserPass + PrivateKey string `json:"privateKey"` +} + +// ImportKey adds a private key to the provided user +func (service *AvaAPI) ImportKey(r *http.Request, args *ImportKeyArgs, reply *api.JsonAddress) error { + service.vm.ctx.Log.Info("Platform: ImportKey called for user '%s'", args.Username) + db, err := service.vm.ctx.Keystore.GetDatabase(args.Username, args.Password) + if err != nil { + return fmt.Errorf("problem retrieving data: %w", err) + } + + user := user{db: db} + + if !strings.HasPrefix(args.PrivateKey, constants.SecretKeyPrefix) { + return fmt.Errorf("private key missing %s prefix", constants.SecretKeyPrefix) + } + sk, err := crypto.ToECDSA(common.FromHex(args.PrivateKey)) + if err != nil { + return fmt.Errorf("invalid private key") + } + if err = user.putAddress(sk); err != nil { + return fmt.Errorf("problem saving key %w", err) + } + + reply.Address, err = service.vm.FormatAddress(crypto.PubkeyToAddress(sk.PublicKey)) + if err != nil { + return fmt.Errorf("problem formatting address: %w", err) + } + return nil +} + +// ImportAVAArgs are the arguments to ImportAVA +type ImportAVAArgs struct { + api.UserPass + // The address that will receive the imported funds + To string `json:"to"` +} + +// ImportAVA returns an unsigned transaction to import AVA from the X-Chain. +// The AVA must have already been exported from the X-Chain. +func (service *AvaAPI) ImportAVA(_ *http.Request, args *ImportAVAArgs, response *api.JsonTxID) error { + service.vm.ctx.Log.Info("Platform: ImportAVA called") + + // Get the user's info + db, err := service.vm.ctx.Keystore.GetDatabase(args.Username, args.Password) + if err != nil { + return fmt.Errorf("couldn't get user '%s': %w", args.Username, err) + } + user := user{db: db} + + to, err := service.vm.ParseAddress(args.To) + if err != nil { // Parse address + return fmt.Errorf("couldn't parse argument 'to' to an address: %w", err) + } + + privKeys, err := user.getKeys() + if err != nil { // Get keys + return fmt.Errorf("couldn't get keys controlled by the user: %w", err) + } + + tx, err := service.vm.newImportTx(to, privKeys) + if err != nil { + return err + } + + response.TxID = tx.ID() + return service.vm.issueTx(tx) +} -- cgit v1.2.3 From 0844c8c6919f6d98ebe8a9501360ef5bc362dff3 Mon Sep 17 00:00:00 2001 From: Determinant Date: Wed, 19 Aug 2020 01:25:20 -0400 Subject: catch up with the new P-Chain cross-chain impl --- plugin/evm/service.go | 56 +++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 41 insertions(+), 15 deletions(-) (limited to 'plugin/evm/service.go') diff --git a/plugin/evm/service.go b/plugin/evm/service.go index 75e7c31..c05b0a4 100644 --- a/plugin/evm/service.go +++ b/plugin/evm/service.go @@ -16,6 +16,8 @@ import ( "github.com/ava-labs/coreth/core/types" "github.com/ava-labs/gecko/api" "github.com/ava-labs/gecko/utils/constants" + avacrypto "github.com/ava-labs/gecko/utils/crypto" + "github.com/ava-labs/gecko/utils/formatting" "github.com/ava-labs/go-ethereum/common" "github.com/ava-labs/go-ethereum/common/hexutil" "github.com/ava-labs/go-ethereum/crypto" @@ -147,13 +149,12 @@ 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.ParseAddress(args.Address); err != nil { + if address, err := service.vm.ParseLocalAddress(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) } else { - reply.PrivateKey = common.ToHex(crypto.FromECDSA(sk)) - //constants.SecretKeyPrefix + formatting.CB58{Bytes: sk.Bytes()}.String() + reply.PrivateKey = constants.SecretKeyPrefix + formatting.CB58{Bytes: sk.Bytes()}.String() return nil } } @@ -167,6 +168,10 @@ type ImportKeyArgs struct { // ImportKey adds a private key to the provided user func (service *AvaAPI) ImportKey(r *http.Request, args *ImportKeyArgs, reply *api.JsonAddress) error { service.vm.ctx.Log.Info("Platform: ImportKey called for user '%s'", args.Username) + if service.vm.ctx.Keystore == nil { + return fmt.Errorf("oh no") + } + fmt.Sprintf("good") db, err := service.vm.ctx.Keystore.GetDatabase(args.Username, args.Password) if err != nil { return fmt.Errorf("problem retrieving data: %w", err) @@ -174,35 +179,56 @@ func (service *AvaAPI) ImportKey(r *http.Request, args *ImportKeyArgs, reply *ap user := user{db: db} + factory := avacrypto.FactorySECP256K1R{} + if !strings.HasPrefix(args.PrivateKey, constants.SecretKeyPrefix) { return fmt.Errorf("private key missing %s prefix", constants.SecretKeyPrefix) } - sk, err := crypto.ToECDSA(common.FromHex(args.PrivateKey)) + trimmedPrivateKey := strings.TrimPrefix(args.PrivateKey, constants.SecretKeyPrefix) + formattedPrivateKey := formatting.CB58{} + if err := formattedPrivateKey.FromString(trimmedPrivateKey); err != nil { + return fmt.Errorf("problem parsing private key: %w", err) + } + + skIntf, err := factory.ToPrivateKey(formattedPrivateKey.Bytes) if err != nil { - return fmt.Errorf("invalid private key") + return fmt.Errorf("problem parsing private key: %w", err) } - if err = user.putAddress(sk); err != nil { + sk := skIntf.(*avacrypto.PrivateKeySECP256K1R) + + if err := user.putAddress(sk); err != nil { return fmt.Errorf("problem saving key %w", err) } - reply.Address, err = service.vm.FormatAddress(crypto.PubkeyToAddress(sk.PublicKey)) + // TODO: return eth address here + reply.Address, err = service.vm.FormatAddress( + crypto.PubkeyToAddress(*(sk.PublicKey().(*avacrypto.PublicKeySECP256K1R).ToECDSA()))) if err != nil { return fmt.Errorf("problem formatting address: %w", err) } return nil } -// ImportAVAArgs are the arguments to ImportAVA -type ImportAVAArgs struct { +// ImportAVAXArgs are the arguments to ImportAVAX +type ImportAVAXArgs struct { api.UserPass + + // Chain the funds are coming from + SourceChain string `json:"sourceChain"` + // The address that will receive the imported funds To string `json:"to"` } -// ImportAVA returns an unsigned transaction to import AVA from the X-Chain. -// The AVA must have already been exported from the X-Chain. -func (service *AvaAPI) ImportAVA(_ *http.Request, args *ImportAVAArgs, response *api.JsonTxID) error { - service.vm.ctx.Log.Info("Platform: ImportAVA called") +// ImportAVAX issues a transaction to import AVAX from the X-chain. The AVAX +// must have already been exported from the X-Chain. +func (service *AvaAPI) ImportAVAX(_ *http.Request, args *ImportAVAXArgs, response *api.JsonTxID) error { + service.vm.ctx.Log.Info("Platform: ImportAVAX called") + + chainID, err := service.vm.ctx.BCLookup.Lookup(args.SourceChain) + if err != nil { + return fmt.Errorf("problem parsing chainID %q: %w", args.SourceChain, err) + } // Get the user's info db, err := service.vm.ctx.Keystore.GetDatabase(args.Username, args.Password) @@ -211,7 +237,7 @@ func (service *AvaAPI) ImportAVA(_ *http.Request, args *ImportAVAArgs, response } user := user{db: db} - to, err := service.vm.ParseAddress(args.To) + to, err := service.vm.ParseLocalAddress(args.To) if err != nil { // Parse address return fmt.Errorf("couldn't parse argument 'to' to an address: %w", err) } @@ -221,7 +247,7 @@ func (service *AvaAPI) ImportAVA(_ *http.Request, args *ImportAVAArgs, response return fmt.Errorf("couldn't get keys controlled by the user: %w", err) } - tx, err := service.vm.newImportTx(to, privKeys) + tx, err := service.vm.newImportTx(chainID, to, privKeys) if err != nil { return err } -- cgit v1.2.3 From ec103c07738b3db70501dee7a7da113edbb68875 Mon Sep 17 00:00:00 2001 From: Determinant Date: Wed, 19 Aug 2020 16:13:57 -0400 Subject: ... --- plugin/evm/service.go | 2 ++ 1 file changed, 2 insertions(+) (limited to 'plugin/evm/service.go') diff --git a/plugin/evm/service.go b/plugin/evm/service.go index c05b0a4..41cce50 100644 --- a/plugin/evm/service.go +++ b/plugin/evm/service.go @@ -21,6 +21,7 @@ import ( "github.com/ava-labs/go-ethereum/common" "github.com/ava-labs/go-ethereum/common/hexutil" "github.com/ava-labs/go-ethereum/crypto" + "github.com/ava-labs/go-ethereum/log" ) const ( @@ -253,5 +254,6 @@ func (service *AvaAPI) ImportAVAX(_ *http.Request, args *ImportAVAXArgs, respons } response.TxID = tx.ID() + log.Info("hey here2") return service.vm.issueTx(tx) } -- cgit v1.2.3 From 1e9599e88a5d88e0090b0ebddfae756e343e605a Mon Sep 17 00:00:00 2001 From: Determinant Date: Wed, 19 Aug 2020 20:32:18 -0400 Subject: make the basic X-to-C logic work --- plugin/evm/service.go | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) (limited to 'plugin/evm/service.go') diff --git a/plugin/evm/service.go b/plugin/evm/service.go index 41cce50..86bc706 100644 --- a/plugin/evm/service.go +++ b/plugin/evm/service.go @@ -16,12 +16,11 @@ import ( "github.com/ava-labs/coreth/core/types" "github.com/ava-labs/gecko/api" "github.com/ava-labs/gecko/utils/constants" - avacrypto "github.com/ava-labs/gecko/utils/crypto" + "github.com/ava-labs/gecko/utils/crypto" "github.com/ava-labs/gecko/utils/formatting" "github.com/ava-labs/go-ethereum/common" "github.com/ava-labs/go-ethereum/common/hexutil" - "github.com/ava-labs/go-ethereum/crypto" - "github.com/ava-labs/go-ethereum/log" + ethcrypto "github.com/ava-labs/go-ethereum/crypto" ) const ( @@ -64,7 +63,7 @@ type Web3API struct{} 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 crypto.Keccak256(input) } +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 @@ -101,7 +100,7 @@ func (api *DebugAPI) SpendGenesis(ctx context.Context, nonce uint64) error { gasLimit := 21000 gasPrice := big.NewInt(1000000000) - genPrivateKey, err := crypto.HexToECDSA(GenesisTestKey[2:]) + genPrivateKey, err := ethcrypto.HexToECDSA(GenesisTestKey[2:]) if err != nil { return err } @@ -180,7 +179,7 @@ func (service *AvaAPI) ImportKey(r *http.Request, args *ImportKeyArgs, reply *ap user := user{db: db} - factory := avacrypto.FactorySECP256K1R{} + factory := crypto.FactorySECP256K1R{} if !strings.HasPrefix(args.PrivateKey, constants.SecretKeyPrefix) { return fmt.Errorf("private key missing %s prefix", constants.SecretKeyPrefix) @@ -195,7 +194,7 @@ func (service *AvaAPI) ImportKey(r *http.Request, args *ImportKeyArgs, reply *ap if err != nil { return fmt.Errorf("problem parsing private key: %w", err) } - sk := skIntf.(*avacrypto.PrivateKeySECP256K1R) + sk := skIntf.(*crypto.PrivateKeySECP256K1R) if err := user.putAddress(sk); err != nil { return fmt.Errorf("problem saving key %w", err) @@ -203,7 +202,7 @@ func (service *AvaAPI) ImportKey(r *http.Request, args *ImportKeyArgs, reply *ap // TODO: return eth address here reply.Address, err = service.vm.FormatAddress( - crypto.PubkeyToAddress(*(sk.PublicKey().(*avacrypto.PublicKeySECP256K1R).ToECDSA()))) + ethcrypto.PubkeyToAddress(*(sk.PublicKey().(*crypto.PublicKeySECP256K1R).ToECDSA()))) if err != nil { return fmt.Errorf("problem formatting address: %w", err) } @@ -254,6 +253,5 @@ func (service *AvaAPI) ImportAVAX(_ *http.Request, args *ImportAVAXArgs, respons } response.TxID = tx.ID() - log.Info("hey here2") return service.vm.issueTx(tx) } -- cgit v1.2.3 From 40879ea67433b73b464bd8b012a9809fbb646cfd Mon Sep 17 00:00:00 2001 From: Determinant Date: Wed, 19 Aug 2020 23:34:12 -0400 Subject: WIP: C-to-X transfer --- plugin/evm/service.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'plugin/evm/service.go') diff --git a/plugin/evm/service.go b/plugin/evm/service.go index 86bc706..3165ca7 100644 --- a/plugin/evm/service.go +++ b/plugin/evm/service.go @@ -201,8 +201,7 @@ func (service *AvaAPI) ImportKey(r *http.Request, args *ImportKeyArgs, reply *ap } // TODO: return eth address here - reply.Address, err = service.vm.FormatAddress( - ethcrypto.PubkeyToAddress(*(sk.PublicKey().(*crypto.PublicKeySECP256K1R).ToECDSA()))) + reply.Address, err = service.vm.FormatAddress(GetEthAddress(sk)) if err != nil { return fmt.Errorf("problem formatting address: %w", err) } -- cgit v1.2.3 From 9f503c997bdb67a40ac2817c6cf0eb780a86f3c1 Mon Sep 17 00:00:00 2001 From: Determinant Date: Thu, 20 Aug 2020 00:14:34 -0400 Subject: add C-to-X RPC --- plugin/evm/service.go | 60 ++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 57 insertions(+), 3 deletions(-) (limited to 'plugin/evm/service.go') 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) +} -- cgit v1.2.3