forked from cerc-io/laconicd-deprecated
RPC unlock Ethermint key and eth_sign (#99)
* Set up personal account api for personal sign * Added unlocking key functionality and attach to eth rpc * Implemented eth_sign * Transform V in sig based on yp and fix bug * Fix lint issue * Remove escape character from comment * Switch error handling to panic on invalid unlocked key
This commit is contained in:
parent
02047bf8bf
commit
6c72a79035
13
rpc/apis.go
13
rpc/apis.go
@ -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,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
@ -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
|
||||
}
|
||||
|
@ -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"
|
||||
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
@ -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.
|
||||
|
34
rpc/personal_api.go
Normal file
34
rpc/personal_api.go
Normal file
@ -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
|
||||
}
|
Loading…
Reference in New Issue
Block a user