Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
evlekht committed Jan 22, 2025
1 parent 798d962 commit 35d82af
Show file tree
Hide file tree
Showing 81 changed files with 11,609 additions and 8 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
build
tests/e2e/e2e.test

.vscode
.idea
Expand Down
4 changes: 2 additions & 2 deletions internal/matrix/matrix_messenger.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ func (m *messenger) StartReceiver() (id.UserID, error) {
return "", err
}

signature, message, err := signPublicKey(m.botKey)
signature, message, err := SignPublicKey(m.botKey)
if err != nil {
return "", err
}
Expand Down Expand Up @@ -206,7 +206,7 @@ func (m *messenger) Inbound() chan types.Message {
return m.msgChannel
}

func signPublicKey(key *ecdsa.PrivateKey) (signature string, message string, err error) {
func SignPublicKey(key *ecdsa.PrivateKey) (signature string, message string, err error) {
pubKeyBytes := crypto.FromECDSAPub(&key.PublicKey)
signatureBytes, err := sign(pubKeyBytes, key)
if err != nil {
Expand Down
10 changes: 5 additions & 5 deletions internal/rpc/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,16 @@ import (
"fmt"
"net"

"github.com/chain4travel/camino-messenger-bot/config"
"github.com/chain4travel/camino-messenger-bot/internal/messaging"
"github.com/chain4travel/camino-messenger-bot/internal/messaging/types"
"github.com/chain4travel/camino-messenger-bot/internal/metadata"
"github.com/chain4travel/camino-messenger-bot/internal/rpc"
"github.com/chain4travel/camino-messenger-bot/internal/rpc/generated"
"github.com/chain4travel/camino-messenger-bot/internal/tracing"
"github.com/chain4travel/camino-messenger-bot/proto/pb/readiness"
"github.com/chain4travel/camino-messenger-bot/utils/tls"

"github.com/chain4travel/camino-messenger-bot/config"
"github.com/chain4travel/camino-messenger-bot/internal/messaging"
"github.com/chain4travel/camino-messenger-bot/internal/metadata"
utils "github.com/chain4travel/camino-messenger-bot/utils/tls"
"go.opentelemetry.io/otel/trace"
"go.uber.org/zap"
"google.golang.org/grpc"
Expand Down Expand Up @@ -52,7 +52,7 @@ func NewServer(
if cfg.Unencrypted {
logger.Warn("Running gRPC server without TLS!")
} else {
creds, err := utils.LoadTLSCredentials(cfg.ServerCertFile, cfg.ServerKeyFile)
creds, err := tls.LoadTLSCredentials(cfg.ServerCertFile, cfg.ServerKeyFile)
if err != nil {
return nil, fmt.Errorf("could not load TLS keys: %w", err)
}
Expand Down
4 changes: 4 additions & 0 deletions tests/e2e/TODO:
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
TODO:
caminogo dependency
custom genesis
maybe 2 node network with network struct
229 changes: 229 additions & 0 deletions tests/e2e/bot/bot.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
package bot

import (
"context"
"fmt"
"io"
"log"
"os"
"os/exec"
"path"
"time"

"buf.build/gen/go/chain4travel/camino-messenger-protocol/grpc/go/cmp/services/accommodation/v1/accommodationv1grpc"
"buf.build/gen/go/chain4travel/camino-messenger-protocol/grpc/go/cmp/services/accommodation/v2/accommodationv2grpc"
"buf.build/gen/go/chain4travel/camino-messenger-protocol/grpc/go/cmp/services/activity/v1/activityv1grpc"
"buf.build/gen/go/chain4travel/camino-messenger-protocol/grpc/go/cmp/services/activity/v2/activityv2grpc"
"buf.build/gen/go/chain4travel/camino-messenger-protocol/grpc/go/cmp/services/book/v1/bookv1grpc"
"buf.build/gen/go/chain4travel/camino-messenger-protocol/grpc/go/cmp/services/book/v2/bookv2grpc"
"buf.build/gen/go/chain4travel/camino-messenger-protocol/grpc/go/cmp/services/info/v1/infov1grpc"
"buf.build/gen/go/chain4travel/camino-messenger-protocol/grpc/go/cmp/services/info/v2/infov2grpc"
"buf.build/gen/go/chain4travel/camino-messenger-protocol/grpc/go/cmp/services/insurance/v1/insurancev1grpc"
"buf.build/gen/go/chain4travel/camino-messenger-protocol/grpc/go/cmp/services/ping/v1/pingv1grpc"
"buf.build/gen/go/chain4travel/camino-messenger-protocol/grpc/go/cmp/services/seat_map/v1/seat_mapv1grpc"
"buf.build/gen/go/chain4travel/camino-messenger-protocol/grpc/go/cmp/services/seat_map/v2/seat_mapv2grpc"
"buf.build/gen/go/chain4travel/camino-messenger-protocol/grpc/go/cmp/services/transport/v1/transportv1grpc"
"buf.build/gen/go/chain4travel/camino-messenger-protocol/grpc/go/cmp/services/transport/v2/transportv2grpc"
"github.com/chain4travel/camino-messenger-bot/config"
"github.com/chain4travel/camino-messenger-bot/proto/pb/readiness"
"github.com/chain4travel/camino-messenger-bot/tests/e2e/process"
"github.com/go-viper/mapstructure/v2"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"gopkg.in/yaml.v3"
)

const botRequestTickerInterval = 500 * time.Millisecond

func StartNewBot(
ctx context.Context,
botDir string,
botBinPath string,
config config.UnparsedConfig,
w io.Writer,
) (*Bot, chan error, error) {
// TODO@ ensure that no other cmb is running on this port ?
if err := os.RemoveAll(botDir); err != nil {
return nil, nil, fmt.Errorf("failed to remove bot data dir: %w", err)
}

if err := os.MkdirAll(botDir, 0755); err != nil {
return nil, nil, fmt.Errorf("failed to create bot data dir: %w", err)
}

unparsedMap := &map[string]any{}
if err := mapstructure.Decode(config, unparsedMap); err != nil {
return nil, nil, fmt.Errorf("failed to parse config into map: %w", err)
}
configBytes, err := yaml.Marshal(unparsedMap)
if err != nil {
return nil, nil, fmt.Errorf("failed to marshal config map into yaml: %w", err)
}

configPath := path.Join(botDir, "config.yaml")

if err := os.WriteFile(configPath, configBytes, 0644); err != nil {
return nil, nil, fmt.Errorf("failed to write config file: %w", err)
}

cmd := exec.Command(botBinPath, "--config", configPath)
cmd.Stdout = w

if err := cmd.Start(); err != nil {
return nil, nil, fmt.Errorf("failed to start bot (%d): %w", cmd.Process.Pid, err)
}

clientConnection, err := grpc.NewClient(
fmt.Sprintf("localhost:%d", config.RPCServer.Port),
grpc.WithTransportCredentials(insecure.NewCredentials()), // TODO@ configure from config
)
if err != nil {
return nil, nil, fmt.Errorf("failed to create grpc client: %w", err)
}

b := &Bot{
pid: cmd.Process.Pid,
rpcClient: newRPCClient(clientConnection),
}

b.awaitReady(ctx)

log.Printf("bot (%d) started\n", cmd.Process.Pid)

return b, process.ListenForProcessError(cmd), nil
}

type Bot struct {
pid int

*rpcClient
}

func (b *Bot) Stop(ctx context.Context) error {
if err := process.StopProcess(ctx, b.pid); err != nil {
return fmt.Errorf("failed to stop cmb process with pid %d: %w", b.pid, err)
}
return nil
}

func (b *Bot) Client() *rpcClient {
return b.rpcClient
}

func (b *Bot) awaitReady(ctx context.Context) error {
ticker := time.NewTicker(botRequestTickerInterval)
defer ticker.Stop()

for {
res, err := b.rpcClient.Readiness(ctx, nil)
if err == nil && res.Status == "ready" {
return nil
}

select {
case <-ticker.C:
case <-ctx.Done():
return ctx.Err()
}
}
}

// TODO@ codegen
func newRPCClient(connection *grpc.ClientConn) *rpcClient {
return &rpcClient{
ReadinessServiceClient: readiness.NewReadinessServiceClient(connection),

accommodationProductInfoV1: accommodationv1grpc.NewAccommodationProductInfoServiceClient(connection),
accommodationProductListV1: accommodationv1grpc.NewAccommodationProductListServiceClient(connection),
accommodationSearchV1: accommodationv1grpc.NewAccommodationSearchServiceClient(connection),

accommodationProductInfoV2: accommodationv2grpc.NewAccommodationProductInfoServiceClient(connection),
accommodationProductListV2: accommodationv2grpc.NewAccommodationProductListServiceClient(connection),
accommodationSearchV2: accommodationv2grpc.NewAccommodationSearchServiceClient(connection),

activityProductInfoV1: activityv1grpc.NewActivityProductInfoServiceClient(connection),
activityProductListV1: activityv1grpc.NewActivityProductListServiceClient(connection),
activitySearchV1: activityv1grpc.NewActivitySearchServiceClient(connection),

activityProductInfoV2: activityv2grpc.NewActivityProductInfoServiceClient(connection),
activityProductListV2: activityv2grpc.NewActivityProductListServiceClient(connection),
activitySearchV2: activityv2grpc.NewActivitySearchServiceClient(connection),

mintV1: bookv1grpc.NewMintServiceClient(connection),
validationV1: bookv1grpc.NewValidationServiceClient(connection),

mintV2: bookv2grpc.NewMintServiceClient(connection),
validationV2: bookv2grpc.NewValidationServiceClient(connection),

countryEntryRequirementsV1: infov1grpc.NewCountryEntryRequirementsServiceClient(connection),

countryEntryRequirementsV2: infov2grpc.NewCountryEntryRequirementsServiceClient(connection),

insuranceProductInfoV1: insurancev1grpc.NewInsuranceProductInfoServiceClient(connection),
insuranceProductListV1: insurancev1grpc.NewInsuranceProductListServiceClient(connection),
insuranceSearchV1: insurancev1grpc.NewInsuranceSearchServiceClient(connection),

pingV1: pingv1grpc.NewPingServiceClient(connection),

seatMapAvailabilityV1: seat_mapv1grpc.NewSeatMapAvailabilityServiceClient(connection),

seatMapAvailabilityV2: seat_mapv2grpc.NewSeatMapAvailabilityServiceClient(connection),

transportSearchV1: transportv1grpc.NewTransportSearchServiceClient(connection),

transportSearchV2: transportv2grpc.NewTransportSearchServiceClient(connection),
}
}

// TODO@ codegen
type rpcClient struct {
readiness.ReadinessServiceClient

// accommodation
accommodationProductInfoV1 accommodationv1grpc.AccommodationProductInfoServiceClient
accommodationProductListV1 accommodationv1grpc.AccommodationProductListServiceClient
accommodationSearchV1 accommodationv1grpc.AccommodationSearchServiceClient

accommodationProductInfoV2 accommodationv2grpc.AccommodationProductInfoServiceClient
accommodationProductListV2 accommodationv2grpc.AccommodationProductListServiceClient
accommodationSearchV2 accommodationv2grpc.AccommodationSearchServiceClient

// activity
activityProductInfoV1 activityv1grpc.ActivityProductInfoServiceClient
activityProductListV1 activityv1grpc.ActivityProductListServiceClient
activitySearchV1 activityv1grpc.ActivitySearchServiceClient

activityProductInfoV2 activityv2grpc.ActivityProductInfoServiceClient
activityProductListV2 activityv2grpc.ActivityProductListServiceClient
activitySearchV2 activityv2grpc.ActivitySearchServiceClient

// book
mintV1 bookv1grpc.MintServiceClient
validationV1 bookv1grpc.ValidationServiceClient

mintV2 bookv2grpc.MintServiceClient
validationV2 bookv2grpc.ValidationServiceClient

// info
countryEntryRequirementsV1 infov1grpc.CountryEntryRequirementsServiceClient

countryEntryRequirementsV2 infov2grpc.CountryEntryRequirementsServiceClient

// insurance
insuranceProductInfoV1 insurancev1grpc.InsuranceProductInfoServiceClient
insuranceProductListV1 insurancev1grpc.InsuranceProductListServiceClient
insuranceSearchV1 insurancev1grpc.InsuranceSearchServiceClient

// ping
pingV1 pingv1grpc.PingServiceClient

// seat map
seatMapAvailabilityV1 seat_mapv1grpc.SeatMapAvailabilityServiceClient

seatMapAvailabilityV2 seat_mapv2grpc.SeatMapAvailabilityServiceClient

// transport
transportSearchV1 transportv1grpc.TransportSearchServiceClient

transportSearchV2 transportv2grpc.TransportSearchServiceClient
}
106 changes: 106 additions & 0 deletions tests/e2e/bot/factory.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package bot

import (
"context"
"crypto/ecdsa"
"crypto/rand"
"encoding/hex"
"fmt"
"io"
"math/big"
"path"
"strconv"

"github.com/chain4travel/camino-messenger-bot/config"
"github.com/chain4travel/camino-messenger-bot/tests/e2e/conduit"
"github.com/chain4travel/camino-messenger-bot/tests/e2e/node"
"github.com/ethereum/go-ethereum/crypto"
)

var DefaultCMAccountOwnerFunds = big.NewInt(0).Mul(node.CAM, big.NewInt(100))

type Factory struct {
dir string
binPath string
migrationsPath string
networkClient *node.Client
matrix *conduit.MatrixServer
}

// NewFactory creates a new bot factory.
func NewFactory(
e2eTmpDir string,
binPath string,
migrationsDir string,
networkClient *node.Client,
matrix *conduit.MatrixServer,
) *Factory {
return &Factory{
dir: path.Join(e2eTmpDir, "cmb"),
binPath: binPath,
migrationsPath: "file://" + migrationsDir,
networkClient: networkClient,
matrix: matrix,
}
}

// CreateBot creates a new bot.
func (f *Factory) CreateBot(ctx context.Context, port uint64, out io.Writer) (*Bot, chan error, error) {
key, err := ecdsa.GenerateKey(crypto.S256(), rand.Reader)
if err != nil {
return nil, nil, fmt.Errorf("failed to generate key: %w", err)
}

ownerAddr := crypto.PubkeyToAddress(key.PublicKey)

cmAccountAddress, _, err := f.networkClient.CreateCMAccount(ctx, key)
if err != nil {
return nil, nil, fmt.Errorf("failed to create CM account: %w", err)
}

if err := f.networkClient.Transfer(ctx, f.networkClient.PrefundedKeys()[0], ownerAddr, DefaultCMAccountOwnerFunds); err != nil {
return nil, nil, fmt.Errorf("failed to transfer funds to cm account owner: %w", err)
}

if err := f.networkClient.AddBotToCMAccount(ctx, cmAccountAddress, key, ownerAddr); err != nil {
return nil, nil, fmt.Errorf("failed to add bot to CM account: %w", err)
}

botDir := path.Join(f.dir, strconv.FormatUint(port, 10)) // TODO@ add some more suffix to make bots form different tests unique 100%

return StartNewBot(
ctx,
botDir,
f.binPath,
config.UnparsedConfig{
DeveloperMode: true,
BotKey: hex.EncodeToString(crypto.FromECDSA(key)),
CMAccountAddress: cmAccountAddress.Hex(),
ChainRPCURL: f.networkClient.ChainRPCURL(),
BookingTokenAddress: f.networkClient.BookingTokenContractAddress().Hex(),
NetworkFeeRecipientBotAddress: f.matrix.NetworkFeeRecipientBotAddress().Hex(),
NetworkFeeRecipientCMAccountAddress: f.matrix.NetworkFeeRecipientCMAccountAddress().Hex(),
ChequeExpirationTime: 3600 * 24 * 30 * 7, // 7 months
MinChequeDurationUntilExpiration: 3600 * 24 * 30 * 6, // 6 months
CashInPeriod: 3600, // 1h
ResponseTimeout: 30000, // 30s
PartnerPlugin: config.PartnerPluginConfig{
Enabled: false,
Host: "",
Unencrypted: true,
},
RPCServer: config.RPCServerConfig{
Enabled: true,
Port: port,
Unencrypted: true,
},
Matrix: config.UnparsedMatrixConfig{Host: f.matrix.Host().String()},
DB: config.UnparsedSQLiteDBConfig{
DBPath: path.Join(botDir, "db"),
MigrationsPath: f.migrationsPath,
},
Tracing: config.TracingConfig{Enabled: false},
},
out,
)
}
1 change: 1 addition & 0 deletions tests/e2e/caminogo/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
We had to copy all those files because of packages incompatiblity. Once we'll update caminogo with latest cortinas, so it will switch to x/exp 2023, the issue will resolve most likely and we can then use caminogo/evm packages.
Loading

0 comments on commit 35d82af

Please sign in to comment.