Skip to content
This repository was archived by the owner on Nov 30, 2021. It is now read-only.
Merged
13 changes: 11 additions & 2 deletions rpc/apis.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,30 @@ package rpc

import (
"github.com/cosmos/cosmos-sdk/client/context"
emintcrypto "github.com/cosmos/ethermint/crypto"
"github.com/ethereum/go-ethereum/rpc"
)

// GetRPCAPIs returns the list of all APIs
func GetRPCAPIs(cliCtx context.CLIContext) []rpc.API {
func GetRPCAPIs(cliCtx context.CLIContext, key emintcrypto.PrivKeySecp256k1) []rpc.API {
return []rpc.API{
{
Namespace: "web3",
Version: "1.0",
Service: NewPublicWeb3API(),
Public: true,
},
{
Namespace: "eth",
Version: "1.0",
Service: NewPublicEthAPI(cliCtx),
Service: NewPublicEthAPI(cliCtx, key),
Public: true,
},
{
Namespace: "personal",
Version: "1.0",
Service: NewPersonalEthAPI(cliCtx),
Public: false,
},
}
}
63 changes: 50 additions & 13 deletions rpc/config.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
package rpc

import (
"fmt"

"github.com/cosmos/cosmos-sdk/client/lcd"
"github.com/cosmos/cosmos-sdk/codec"
emintcrypto "github.com/cosmos/ethermint/crypto"
emintkeys "github.com/cosmos/ethermint/keys"
"github.com/ethereum/go-ethereum/rpc"

"github.com/spf13/cobra"
"log"
"github.com/spf13/viper"
)

// defaultModules returns all available modules
func defaultModules() []string {
return []string{"web3", "eth"}
}
const (
flagUnlockKey = "unlock-key"
)

// Config contains configuration fields that determine the behavior of the RPC HTTP server.
// TODO: These may become irrelevant if HTTP config is handled by the SDK
Expand All @@ -30,31 +34,64 @@ type Config struct {

// Web3RpcCmd creates a CLI command to start RPC server
func Web3RpcCmd(cdc *codec.Codec) *cobra.Command {
return lcd.ServeCommand(cdc, registerRoutes)
cmd := lcd.ServeCommand(cdc, registerRoutes)
// Attach flag to cmd output to be handled in registerRoutes
cmd.Flags().String(flagUnlockKey, "", "Select a key to unlock on the RPC server")
return cmd
}

// registerRoutes creates a new server and registers the `/rpc` endpoint.
// Rpc calls are enabled based on their associated module (eg. "eth").
func registerRoutes(rs *lcd.RestServer) {
s := rpc.NewServer()
apis := GetRPCAPIs(rs.CliCtx)
accountName := viper.GetString(flagUnlockKey)

var emintKey emintcrypto.PrivKeySecp256k1
if len(accountName) > 0 {
passphrase, err := emintkeys.GetPassphrase(accountName)
if err != nil {
panic(err)
}

emintKey, err = unlockKeyFromNameAndPassphrase(accountName, passphrase)
if err != nil {
panic(err)
}
}

apis := GetRPCAPIs(rs.CliCtx, emintKey)

// TODO: Allow cli to configure modules https://github.com/ChainSafe/ethermint/issues/74
modules := defaultModules()
whitelist := make(map[string]bool)
for _, module := range modules {
whitelist[module] = true
}

// Register all the APIs exposed by the services
for _, api := range apis {
if whitelist[api.Namespace] || (len(whitelist) == 0 && api.Public) {
if err := s.RegisterName(api.Namespace, api.Service); err != nil {
log.Println(err)
return
panic(err)
}
}
}

rs.Mux.HandleFunc("/rpc", s.ServeHTTP).Methods("POST")
}

func unlockKeyFromNameAndPassphrase(accountName, passphrase string) (emintKey emintcrypto.PrivKeySecp256k1, err error) {
keybase, err := emintkeys.NewKeyBaseFromHomeFlag()
if err != nil {
return
}

privKey, err := keybase.ExportPrivateKeyObject(accountName, passphrase)
if err != nil {
return
}

var ok bool
emintKey, ok = privKey.(emintcrypto.PrivKeySecp256k1)
if !ok {
panic(fmt.Sprintf("invalid private key type: %T", privKey))
}

return
}
23 changes: 20 additions & 3 deletions rpc/eth_api.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
package rpc

import (
"bytes"
"fmt"
"math/big"

"github.com/cosmos/cosmos-sdk/client/context"
emintcrypto "github.com/cosmos/ethermint/crypto"
emintkeys "github.com/cosmos/ethermint/keys"
"github.com/cosmos/ethermint/version"
"github.com/cosmos/ethermint/x/evm/types"

"github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/rpc"
Expand All @@ -17,12 +21,14 @@ import (
// PublicEthAPI is the eth_ prefixed set of APIs in the Web3 JSON-RPC spec.
type PublicEthAPI struct {
cliCtx context.CLIContext
key emintcrypto.PrivKeySecp256k1
}

// NewPublicEthAPI creates an instance of the public ETH Web3 API.
func NewPublicEthAPI(cliCtx context.CLIContext) *PublicEthAPI {
func NewPublicEthAPI(cliCtx context.CLIContext, key emintcrypto.PrivKeySecp256k1) *PublicEthAPI {
return &PublicEthAPI{
cliCtx: cliCtx,
key: key,
}
}

Expand Down Expand Up @@ -176,8 +182,19 @@ func (e *PublicEthAPI) GetCode(address common.Address, blockNumber rpc.BlockNumb
}

// Sign signs the provided data using the private key of address via Geth's signature standard.
func (e *PublicEthAPI) Sign(address common.Address, data hexutil.Bytes) hexutil.Bytes {
return nil
func (e *PublicEthAPI) Sign(address common.Address, data hexutil.Bytes) (hexutil.Bytes, error) {
// TODO: Change this functionality to find an unlocked account by address
if e.key == nil || !bytes.Equal(e.key.PubKey().Address().Bytes(), address.Bytes()) {
return nil, keystore.ErrLocked
}

// Sign the requested hash with the wallet
signature, err := e.key.Sign(data)
if err == nil {
signature[64] += 27 // Transform V from 0/1 to 27/28 according to the yellow paper
}

return signature, err
}

// SendTransaction sends an Ethereum transaction.
Expand Down
34 changes: 34 additions & 0 deletions rpc/personal_api.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package rpc

import (
"context"

sdkcontext "github.com/cosmos/cosmos-sdk/client/context"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
)

// PersonalEthAPI is the eth_ prefixed set of APIs in the Web3 JSON-RPC spec.
type PersonalEthAPI struct {
cliCtx sdkcontext.CLIContext
}

// NewPersonalEthAPI creates an instance of the public ETH Web3 API.
func NewPersonalEthAPI(cliCtx sdkcontext.CLIContext) *PersonalEthAPI {
return &PersonalEthAPI{
cliCtx: cliCtx,
}
}

// Sign calculates an Ethereum ECDSA signature for:
// keccack256("\x19Ethereum Signed Message:\n" + len(message) + message))
//
// Note, the produced signature conforms to the secp256k1 curve R, S and V values,
// where the V value will be 27 or 28 for legacy reasons.
//
// The key used to calculate the signature is decrypted with the given password.
//
// https://github.com/ethereum/go-ethereum/wiki/Management-APIs#personal_sign
func (e *PersonalEthAPI) Sign(ctx context.Context, data hexutil.Bytes, addr common.Address, passwd string) (hexutil.Bytes, error) {
return nil, nil
}