Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Orchestrator gRPC API #1771

Merged
merged 11 commits into from
Apr 8, 2020
8 changes: 4 additions & 4 deletions cmd/mesg-cli/sign.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package main

import (
"bufio"
"encoding/hex"
"encoding/base64"
"fmt"

"github.com/cosmos/cosmos-sdk/client/flags"
Expand Down Expand Up @@ -30,7 +30,7 @@ func signCommand() *cobra.Command {
if err != nil {
return err
}
fmt.Println(hex.EncodeToString(signature))
fmt.Println(base64.RawStdEncoding.EncodeToString(signature))
return nil
},
}
Expand All @@ -41,7 +41,7 @@ func signCommand() *cobra.Command {

func verifySignCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "verifySign <name> <message>",
Use: "verifySign <name> <message> <signature>",
Short: "Verify the signature of the given message",
Args: cobra.MinimumNArgs(3),
RunE: func(cmd *cobra.Command, args []string) error {
Expand All @@ -57,7 +57,7 @@ func verifySignCommand() *cobra.Command {
if err != nil {
return err
}
signature, err := hex.DecodeString(args[2])
signature, err := base64.RawStdEncoding.DecodeString(args[2])
if err != nil {
return err
}
Expand Down
49 changes: 17 additions & 32 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,21 @@ import (
"path/filepath"
"time"

sdkcosmos "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/go-bip39"
"github.com/go-playground/validator/v10"
"github.com/mesg-foundation/engine/ext/xvalidator"
homedir "github.com/mitchellh/go-homedir"
"github.com/sirupsen/logrus"
tmconfig "github.com/tendermint/tendermint/config"
"gopkg.in/yaml.v2"
)

const (
// CosmosBech32MainPrefix defines the main Bech32 prefix.
CosmosBech32MainPrefix = "mesgtest"

// CosmosCoinType is the mesg registered coin type from https://github.com/satoshilabs/slips/blob/master/slip-0044.md.
CosmosCoinType = uint32(470)
)

const (
defaultConfigFileName = "config.yml"
envPathKey = "MESG_PATH"
Expand All @@ -31,6 +37,9 @@ type Config struct {

DefaultExecutionPrice string `validate:"required"`

// AuthorizedPubKeys are the bech32 public key of the accounts that are authorized to call the gRPC Admin API.
AuthorizedPubKeys []string `validate:"dive,required,bech32accpubkey" yaml:"authorized_pubkeys"`

Server struct {
Address string `validate:"required"`
}
Expand All @@ -50,36 +59,27 @@ type Config struct {
RelativePath string `validate:"required"`

// Minimum gas prices for transactions.
MinGasPrices string `validate:"required"`
MinGasPrices string `validate:"required,deccoins"`

// Token name to use in the staking module.
StakeTokenDenom string `validate:"required"`

// Bech32MainPrefix defines the main Bech32 prefix.
Bech32MainPrefix string `validate:"required"`

// CoinType is the mesg registered coin type from https://github.com/satoshilabs/slips/blob/master/slip-0044.md.
CoinType uint32 `validate:"required"`

// BIP44Prefix is the parts of the BIP44 HD path that are fixed by what we used during the fundraiser.
FullFundraiserPath string `validate:"required"`

// Power reduction between the staking token and the voting power on tendermint.
PowerReduction int64 `validate:"required"`
}

DevGenesis struct {
ChainID string `validate:"required"`
InitialBalances string `validate:"required"`
ValidatorDelegationCoin string `validate:"required"`
InitialBalances string `validate:"required,coins"`
ValidatorDelegationCoin string `validate:"required,coin"`
}

Account struct {
Name string `validate:"required"`
Password string `validate:"required"`
Number uint32
Index uint32
Mnemonic string
Mnemonic string `validate:"omitempty,mnemonic"`
}
}

Expand Down Expand Up @@ -118,9 +118,6 @@ func defaultConfig() (*Config, error) {
c.Cosmos.RelativePath = "cosmos"
c.Cosmos.MinGasPrices = "1.0atto"
c.Cosmos.StakeTokenDenom = "atto"
c.Cosmos.Bech32MainPrefix = "mesgtest"
c.Cosmos.CoinType = 470
c.Cosmos.FullFundraiserPath = "44'/470'/0'/0/0" // TODO: is it really useful?
c.Cosmos.PowerReduction = 18

c.DevGenesis.ChainID = "mesg-dev-chain"
Expand Down Expand Up @@ -197,20 +194,8 @@ func (c *Config) validate() error {
if _, err := logrus.ParseLevel(c.Log.Level); err != nil {
return fmt.Errorf("config log.level error: %w", err)
}
if c.Account.Mnemonic != "" && !bip39.IsMnemonicValid(c.Account.Mnemonic) {
return fmt.Errorf("config account.mnemonic error: mnemonic is not valid")
}
if err := c.Tendermint.Config.ValidateBasic(); err != nil {
return fmt.Errorf("config tendermint error: %w", err)
}
if _, err := sdkcosmos.ParseDecCoins(c.Cosmos.MinGasPrices); err != nil {
return fmt.Errorf("config cosmos.mingasprices error: %w", err)
}
if _, err := sdkcosmos.ParseCoins(c.DevGenesis.InitialBalances); err != nil {
return fmt.Errorf("config devgenesis.initialbalances error: %w", err)
}
if _, err := sdkcosmos.ParseCoin(c.DevGenesis.ValidatorDelegationCoin); err != nil {
return fmt.Errorf("config devgenesis.validatordelegationcoin error: %w", err)
}
return validator.New().Struct(c)
return xvalidator.Struct(c)
}
2 changes: 1 addition & 1 deletion core/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ func main() {
}

// init gRPC server.
server := grpc.New(mc, ep, b, cfg.DefaultExecutionPrice)
server := grpc.New(mc, ep, b, cfg.DefaultExecutionPrice, cfg.AuthorizedPubKeys)
logrus.WithField("module", "main").Infof("starting MESG Engine version %s", version.Version)
defer func() {
logrus.WithField("module", "main").Info("stopping grpc server")
Expand Down
34 changes: 18 additions & 16 deletions cosmos/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,32 +9,34 @@ import (
"github.com/mesg-foundation/engine/config"
)

// CustomizeConfig customizes the cosmos application like addresses prefixes and coin type
func CustomizeConfig(engineCfg *config.Config) {
func init() {
// See github.com/cosmos/cosmos-sdk/types/address.go
var (
const (
// Bech32PrefixAccAddr defines the Bech32 prefix of an account's address
Bech32PrefixAccAddr = engineCfg.Cosmos.Bech32MainPrefix
Bech32PrefixAccAddr = config.CosmosBech32MainPrefix
// Bech32PrefixAccPub defines the Bech32 prefix of an account's public key
Bech32PrefixAccPub = engineCfg.Cosmos.Bech32MainPrefix + sdktypes.PrefixPublic
Bech32PrefixAccPub = config.CosmosBech32MainPrefix + sdktypes.PrefixPublic
// Bech32PrefixValAddr defines the Bech32 prefix of a validator's operator address
Bech32PrefixValAddr = engineCfg.Cosmos.Bech32MainPrefix + sdktypes.PrefixValidator + sdktypes.PrefixOperator
Bech32PrefixValAddr = config.CosmosBech32MainPrefix + sdktypes.PrefixValidator + sdktypes.PrefixOperator
// Bech32PrefixValPub defines the Bech32 prefix of a validator's operator public key
Bech32PrefixValPub = engineCfg.Cosmos.Bech32MainPrefix + sdktypes.PrefixValidator + sdktypes.PrefixOperator + sdktypes.PrefixPublic
Bech32PrefixValPub = config.CosmosBech32MainPrefix + sdktypes.PrefixValidator + sdktypes.PrefixOperator + sdktypes.PrefixPublic
// Bech32PrefixConsAddr defines the Bech32 prefix of a consensus node address
Bech32PrefixConsAddr = engineCfg.Cosmos.Bech32MainPrefix + sdktypes.PrefixValidator + sdktypes.PrefixConsensus
Bech32PrefixConsAddr = config.CosmosBech32MainPrefix + sdktypes.PrefixValidator + sdktypes.PrefixConsensus
// Bech32PrefixConsPub defines the Bech32 prefix of a consensus node public key
Bech32PrefixConsPub = engineCfg.Cosmos.Bech32MainPrefix + sdktypes.PrefixValidator + sdktypes.PrefixConsensus + sdktypes.PrefixPublic
Bech32PrefixConsPub = config.CosmosBech32MainPrefix + sdktypes.PrefixValidator + sdktypes.PrefixConsensus + sdktypes.PrefixPublic
)

config := sdktypes.GetConfig()
config.SetBech32PrefixForAccount(Bech32PrefixAccAddr, Bech32PrefixAccPub)
config.SetBech32PrefixForValidator(Bech32PrefixValAddr, Bech32PrefixValPub)
config.SetBech32PrefixForConsensusNode(Bech32PrefixConsAddr, Bech32PrefixConsPub)
config.SetFullFundraiserPath(engineCfg.Cosmos.FullFundraiserPath)
config.SetCoinType(engineCfg.Cosmos.CoinType)
config.Seal()
cfg := sdktypes.GetConfig()
cfg.SetBech32PrefixForAccount(Bech32PrefixAccAddr, Bech32PrefixAccPub)
cfg.SetBech32PrefixForValidator(Bech32PrefixValAddr, Bech32PrefixValPub)
cfg.SetBech32PrefixForConsensusNode(Bech32PrefixConsAddr, Bech32PrefixConsPub)
// cfg.SetFullFundraiserPath(config.CosmosFullFundraiserPath)
cfg.SetCoinType(config.CosmosCoinType)
cfg.Seal()
}

// CustomizeConfig customizes the cosmos application like addresses prefixes and coin type
func CustomizeConfig(engineCfg *config.Config) {
// From github.com/cosmos/cosmos-sdk/types/staking.go
// Set the power reduction from token to voting power to 10^18 (number of decimal of the token).
sdktypes.PowerReduction = sdktypes.NewIntFromBigInt(new(big.Int).Exp(big.NewInt(10), big.NewInt(engineCfg.Cosmos.PowerReduction), nil))
Expand Down
94 changes: 75 additions & 19 deletions e2e/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package main

import (
"context"
"encoding/base64"
"fmt"
"os"
"path/filepath"
Expand All @@ -12,6 +13,7 @@ import (
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/crypto/keys"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/bank"
"github.com/mesg-foundation/engine/app"
"github.com/mesg-foundation/engine/config"
"github.com/mesg-foundation/engine/container"
Expand All @@ -20,36 +22,45 @@ import (
"github.com/mesg-foundation/engine/ext/xnet"
"github.com/mesg-foundation/engine/hash"
pb "github.com/mesg-foundation/engine/protobuf/api"
"github.com/mesg-foundation/engine/server/grpc/orchestrator"
"github.com/stretchr/testify/require"
"google.golang.org/grpc"
)

type apiclient struct {
pb.EventClient
EventClient pb.EventClient
// TODO: to switch when e2e tests doesn't create any event
// EventClient orchestrator.EventClient
Comment on lines +32 to +33
Copy link
Member Author

@NicolasMahe NicolasMahe Apr 6, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ExecutionClient orchestrator.ExecutionClient
RunnerClient orchestrator.RunnerClient
}

var (
minExecutionPrice sdk.Coins
client apiclient
client *apiclient
cdc = app.MakeCodec()
processInitialBalance = sdk.NewCoins(sdk.NewInt64Coin("atto", 10000000))
kb *cosmos.Keybase
cfg *config.Config
engineAddress sdk.AccAddress
engineAccountName string
engineAccountPassword string
cont container.Container
ipfsEndpoint string
engineName string
enginePort string
lcd *cosmos.LCD
lcdEngine *cosmos.LCD
cliAddress sdk.AccAddress
cliInitialBalance, _ = sdk.ParseCoins("100000000000000000000000000atto")
)

const (
lcdEndpoint = "http://127.0.0.1:1317/"
pollingInterval = 500 * time.Millisecond // half a block
pollingTimeout = 10 * time.Second // 10 blocks
gasLimit = flags.DefaultGasLimit * 10 // x10 so the biggest txs have enough gas
lcdEndpoint = "http://127.0.0.1:1317/"
pollingInterval = 500 * time.Millisecond // half a block
pollingTimeout = 10 * time.Second // 10 blocks
gasLimit = flags.DefaultGasLimit * 10 // x10 so the biggest txs have enough gas
cliAccountMnemonic = "large fork soccer lab answer enlist robust vacant narrow please inmate primary father must add hub shy couch rail video tool marine pill give"
cliAccountName = "cli"
cliAccountPassword = "pass"
)

func TestAPI(t *testing.T) {
Expand All @@ -70,16 +81,24 @@ func TestAPI(t *testing.T) {
err = os.MkdirAll(filepath.Join(cfg.Path, cfg.Cosmos.RelativePath), os.FileMode(0755))
require.NoError(t, err)

// init keybase with account
// init keybase with engine account and cli account
kb, err = cosmos.NewKeybase(filepath.Join(cfg.Path, cfg.Cosmos.RelativePath))
require.NoError(t, err)
if cfg.Account.Mnemonic != "" {
acc, err := kb.CreateAccount(cfg.Account.Name, cfg.Account.Mnemonic, "", cfg.Account.Password, keys.CreateHDPath(cfg.Account.Number, cfg.Account.Index).String(), cosmos.DefaultAlgo)
require.NoError(t, err)
engineAddress = acc.GetAddress()
engineAccountName = cfg.Account.Name
engineAccountPassword = cfg.Account.Password
}
// init engine account
engineAcc, err := kb.CreateAccount(cfg.Account.Name, cfg.Account.Mnemonic, "", cfg.Account.Password, keys.CreateHDPath(cfg.Account.Number, cfg.Account.Index).String(), cosmos.DefaultAlgo)
require.NoError(t, err)
engineAddress = engineAcc.GetAddress()

// init cli account
cliAcc, err := kb.CreateAccount(cliAccountName, cliAccountMnemonic, "", cliAccountPassword, keys.CreateHDPath(cfg.Account.Number, cfg.Account.Index).String(), cosmos.DefaultAlgo)
require.NoError(t, err)
cliAddress = cliAcc.GetAddress()

// init LCD with engine account and make a transfer to cli account
lcdEngine, err = cosmos.NewLCD(lcdEndpoint, cdc, kb, cfg.DevGenesis.ChainID, cfg.Account.Name, cfg.Account.Password, cfg.Cosmos.MinGasPrices, gasLimit)
require.NoError(t, err)
_, err = lcdEngine.BroadcastMsg(bank.NewMsgSend(engineAddress, cliAddress, cliInitialBalance))
require.NoError(t, err)

// init container
cont, err = container.New(cfg.Name)
Expand All @@ -93,12 +112,16 @@ func TestAPI(t *testing.T) {
conn, err := grpc.DialContext(context.Background(), "localhost:50052", grpc.WithInsecure())
require.NoError(t, err)

client = apiclient{
pb.NewEventClient(conn),
client = &apiclient{
EventClient: pb.NewEventClient(conn),
// TODO: to switch when e2e tests doesn't create any event
// EventClient: orchestrator.NewEventClient(conn),
ExecutionClient: orchestrator.NewExecutionClient(conn),
RunnerClient: orchestrator.NewRunnerClient(conn),
}

// init LCD
lcd, err = cosmos.NewLCD(lcdEndpoint, cdc, kb, cfg.DevGenesis.ChainID, cfg.Account.Name, cfg.Account.Password, cfg.Cosmos.MinGasPrices, gasLimit)
lcd, err = cosmos.NewLCD(lcdEndpoint, cdc, kb, cfg.DevGenesis.ChainID, cliAccountName, cliAccountPassword, cfg.Cosmos.MinGasPrices, gasLimit)
require.NoError(t, err)

// run tests
Expand Down Expand Up @@ -152,3 +175,36 @@ func pollExecutionOfProcess(processHash hash.Hash, status execution.Status, node
}
}
}

func signPayload(payload interface{}) ([]byte, error) {
encodedValue, err := cdc.MarshalJSON(payload)
if err != nil {
return nil, err
}
signature, _, err := kb.Sign(cliAccountName, cliAccountPassword, encodedValue)
if err != nil {
return nil, err
}
return signature, nil
}

// signCred is a structure that manage a token.
type signCred struct {
request interface{}
}

// GetRequestMetadata returns the metadata for the request.
func (c *signCred) GetRequestMetadata(context.Context, ...string) (map[string]string, error) {
signature, err := signPayload(c.request)
if err != nil {
return nil, err
}
return map[string]string{
orchestrator.RequestSignature: base64.RawStdEncoding.EncodeToString(signature),
}, nil
}

// RequireTransportSecurity tells if the transport should be secured.
func (c *signCred) RequireTransportSecurity() bool {
return false
}
Loading