-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
81 changed files
with
11,609 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
build | ||
tests/e2e/e2e.test | ||
|
||
.vscode | ||
.idea | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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, | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. |
Oops, something went wrong.