Как считать токены для GPT-3/GPT-4

Как считать токены для GPT-3/GPT-4 : GPT-Tokenator

Как считать токены для GPT-3/GPT-4 : GPT-Tokenator

OpenAI предоставляет мощные инструменты для работы с GPT-3 и GPT-4. Однако возможность подсчёта токенов реализована только для JavaScript и Python, что не покрывает всех возможных вариантов использования. В связи с этим я разработал универсальную библиотеку GPT-Tokenator для подсчёта токенов на C++, экспортировал функцию подсчёта токенов в C, что даёт возможность использовать её во многих языках программирования.


Необходимость подсчёта токенов возникает во множестве прикладных задач.

Вот лишь несколько очевидных примеров:

  • При анализе большого текста разбивать текст на части оптимальным образом, чтобы за один раз отправить на обработку максимально большой фрагмент текста.

  • Оценивать превышение лимита токенов до отправки запроса к API.

  • Оптимально передавать параметр max_tokens.

Кажется, что подсчёт токенов настолько базовая функциональность, что выглядит очень странным, что OpenAI не предоставил средства для этого.

Основные особенности и преимущества GPT-Tokenator

GPT-Tokenator предлагает следующие ключевые особенности и преимущества:

  • Поддержка C++ и экспорт в C для использования во многих языках программирования.

  • Простые и легко используемые функции для подсчёта токенов.

  • Примеры использования библиотеки на разных языках программирования.

  • Отсутствие необходимости использовать словари преобразования. Всё необходимое встроено в саму библиотеку.

Я специально не стал включать функции кодирования и декодирования токенов, чтобы сделать бинарный файл библиотеки настолько лёгким, насколько это возможно. По сути, в версии на C имеется только одна простая функция:

size_t tokenator_count(const char* data, size_t len);

Выбор C очевиден, потому что API C в том или ином виде поддерживается всеми языками программирования.

Примеры использования GPT-Tokenator

Я подготовил несколько примеров использования GPT-Tokenator на разных языках программирования:

Эти примеры помогут вам легко понять, как использовать библиотеку в ваших проектах. Также я надеюсь, они помогут понять, как подключать библиотеку к проектам на языках программирования, не перечисленных в списке выше.

Если возникнут вопросы, не стесняйтесь писать мне. Постараюсь помочь.

Буду очень признателен, если пришлёте PR с примерами кода на языках, не перечисленных в этом списке. Разумеется, можете рассчитывать на мою помощь. Интерфейс самой функции подсчёта токенов очень простой, и единственная сложность, которая может у вас возникнуть — это линковка.

Обратите внимание, что во всех примерах D, Go, C# линковка осуществляется похожим образом.

Установка и компиляция GPT-Tokenator

Для установки и компиляции GPT-Tokenator выполните следующие шаги:

  1. Установите зависимости:

sudo apt-get install libicu-dev
  1. Перейдите в каталог с исходными кодами и выполните команду:

cd src make

Компиляция может занять некоторое время, поэтому для вашего удобства я добавил сжатые предварительно скомпилированные файлы в каталог libs:

  • libtokenator.a

  • libtokenator_cpp.a

Файлы заголовков находятся в каталоге include.

GPT-Tokenator — это универсальная библиотека для подсчёта токенов в GPT-3 и GPT-4 на C/C++, которая может быть полезна для разработчиков, использующих разные языки программирования. Попробуйте GPT-Tokenator в своих проектах и не стесняйтесь отправлять отзывы, предложения по улучшению и сообщения об ошибках через GitHub.

Readme на русском

Исходный код проекта GPT-Tokenator на GitHub


ссылка на оригинал статьи https://habr.com/ru/articles/741068/

Почему человек стал человеком? Из-за мозга или ягодичной мышцы?

Часто можно услышать утверждение: «у человека нет никаких выдающихся физических качеств, он не самый быстрый, не самый сильный, не самый высокий, но стал доминирующим видом благодаря развитому мозгу». Так ли это? Похоже, что да! Именно способность мозга обучаться, накапливать опыт и затем его передавать сделала нас теми, кто мы есть сегодня. Но был ли мозг тем самым game changer, который помог нам занять свою экологическую нишу, выжить и развиться до такой степени? Антропологи Dennis Bramble и Daniel Lieberman сделали занятный материал, в котором предлагают взглянуть на наши способности и эволюционную доминацию с другой стороны. Давай ознакомимся с их трудом, чтобы понять, что они имеют ввиду.

Встали на ноги, чтобы ходить или бегать?

«Хотя прямохождение включает в себя и бег, и ходьбу, принято считать, что бег не сыграл большой роли в эволюции человека, потому что люди, как и обезьяны, являются плохими спринтерами по сравнению с большинством четвероногих».

Ученых сподвигло то, что биомеханика бега человека так хорошо изучена и при этом так мало исследований на тему того, был ли это основной способ передвижения у предков. А изучать тут есть что.

«Этот недостаток внимания во многом объясняется тем, что люди являются посредственными бегунами в нескольких отношениях.»

Даже элитные спринтеры сравнительно медлительны. Максимальную скорость, которую они могут развивать всего 10,2 м/с и поддерживать ее менее 15 секунд. (Прим. максимальная скорость, которую удалось развить человеку, была 12,2 м/с и этот рекорд принадлежит Усейн Болту.)

А вот быстрые четвероногие, такие как лошади, борзые и антилопы, могут поддерживать максимальную скорость галопа 15-20 м/с в течение нескольких минут и преодолевать несколько километров за это время.

Более того, бег обходится людям дороже, чем большинству млекопитающих, требуя примерно в два раза больше метаболической энергии на пройденное расстояние, чем для млекопитающего с поправкой массу тела.

Наконец, люди менее маневренны и лишены многих структурных бонусов, характерных для большинства четвероногих, таких как удлиненные ступни и короткие голени.

Однако, хотя люди сравнительно плохие спринтеры, они занимаются еще одним видом бега — бегом на выносливость, определяемый как пробег многих километров в течение длительных периодов времени с использованием аэробного метаболизма.«

Кто быстрее: лошадь или человек?

Ходьба достаточно затратный ресурс. Но как только мы с ходьбы 1-1,3 м/с переходим на легкий бег 2-2,5 м/с, все резко меняется. При беге энергии тратится столько же, как при ходьбе, но преодолевается вдвое большее расстояние за меньшее время.

Все благодаря комплексу сухожилий и других пружинящих механик. Например, когда мы идем, то ставим вперед практически прямую ногу, которая поглощает в себя всю созданную энергию предыдущим толчком ноги. Перекатывание с пятки на носок затрачивает большой энергетический ресурс, вовлекая в работу икроножные мышцы. Неудачное расположение центра массы ровно посреди туловища. Все это заставляет тратить много энергии. Ходьба больше похожа на маятник, а ноги сопротивляются раскачивающемуся туда-сюда туловищу.

Во время бега тело работает иначе благодаря эластичным способностям сухожилий и более удачному положению ног и корпуса. Сухожилия выталкивают ногу вперед, почти не тратя энергию икроножных мышц на сокращение. Удар приходится на согнутую ногу, что дополнительно задействует коленные связки и сухожилия, что опять снижает нагрузку на мышцы и задействует пружинистые возможности связок. А корпус наклонен слегка вперед, что упрощает его разгон и поддержание скорости. Раскачивание во время бега значительно ниже, чем при ходьбе.

Все это позволяет не только преодолевать большие расстояния, но и двигаться с приличной скоростью. Скорость бега на выносливость у человека приблизительно равна галопу больших (и маленьких) четвероногих.

В Уэльсе с 1980 года ежегодно проходит Марафон человека и лошади. Там люди соревнуются против наездников на лошадях на дистанции 35 км. Чаще побеждают лошади, но было два года (2004 и 2007), когда человек победил. А в 2007 году человек даже дал фору самой быстрой лошади в целых 11 минут.

Но это прохладный тенистый Уэльс, в котором лошадь не так быстро нагревается. Становление нашей цивилизации происходило в жаркой саване, где у нас было еще одно преимущество.

Терморегуляция.

Во время бега производился очень много тепла из-за чего тело нагревается, нарушая внутренний гомеостаз. И в том, как мы умеем остужать тело, нам тоже нет равных.

Система потоотделения есть у большинства животных. Но у нас нет волосяного покрова, что делает потоотделение более эффективным.

Узкое и вытянутое тело тоже является положительным признаком для быстрого остужения. У нас отличное устройство кровеносной и артериальной системы особенно в голове, которая очень эффективно отводит тепло не допуская нагревание мозга. Сонная артерия достаточно близка к поверхности кожи и может успевать остужать кровь, прежде чем доставить ее в мозг.

Еще одной особенностью людей является склонность
дышать ртом (но не задыхаться) во время напряженной деятельности.

Носовое дыхание, типичное для обезьян, оказывает слишком сильное сопротивление воздуху. Ноздри недостаточно широки, чтобы выдерживать высокие требования к вентиляции при интенсивной деятельности. Дыхание ртом обеспечивает более высокую скорость воздушного потока при меньшем сопротивлении и мышечных усилиях. А еще является более эффективным средством разгрузки избыточного тепла во время выдоха.

Все вышеперечисленные особенности делают нас монстрами выносливости, недоступной никаким другим млекопитающим особям.

Я веду Телеграм канал Терентьев Фитнес про тренировки, питание, спортивный лайфстайл. Даю полезные рекомендации для тренирующихся, делюсь опытом подопечных. Подписывайся! Буду рад тебя видеть.Загон животных — до сих пор один из актуальных способов охоты у современных племен охотников и собирателей. Группа охотников выбирает жертву, пугают ее. Та в свою очередь на больших мощностях под палящим солнцем убегает. По следам ее отслеживают и снова пугают. И так до тех пор, пока косуля не останется без сил, чтобы спокойно убить ее с ближнего расстояния.

Ученые строят предположение, что в отличии от тигров, волков, гиен, люди питались более свежими особями, выигрывав еще и в этом. Остальные, выдав спринт, максимум на что могли рассчитывать — это старая больная и хромая косуля. А если ничего не ловили, голодали следующую неделю, восстанавливая силы. Т.е. хищная охота больше похожа на лотерею, а выносливость похожа на расчет.

Итог.

Это крупицы из тех фактов, которые нам предоставили ученые. Они разбирали так же устройство ягодичной мышцы, отмечали ее развитость и эффективность именно при беге в отличии от ходьбы, где она практически не участвует. Развитость ягодичной значительно отличает нас от обезьян, которые тоже умеют достаточно быстро бегать, но не долго, в т.ч. из-за маленькой не развитой ягодичной.

Так же они сравнивали различные остатки костей, которые удалось найти. Сравнивали предполагаемые периоды жизни и развитость костей, их отличия от обезьян, первых людей и нас.

И подводя итог, вполне вероятно, что комплекс выносливости, терморегуляции и бег являлись нашей экологической нишей до того, как начал активно развиваться мозг. Помогали выживать, добывать пропитание и преодолевать большие расстояния в поиске лучших мест для жизни. Возможно, бег и дал мозгу толчок для роста.

Когда тебе станет трудно, помни, что ты чертова машина выносливости!

Я веду Телеграм канал Терентьев Фитнес про тренировки, питание, спортивный лайфстайл. Даю полезные рекомендации для тренирующихся, делюсь опытом подопечных. Подписывайся! Буду рад тебя видеть.


ссылка на оригинал статьи https://habr.com/ru/articles/741072/

Пишем gRPC автотесты на Go с Allure отчетом

Вступление

В данной статье разберем, как писать gRPC автотесты с использованием языка Go, также сделаем Allure отчет

Перед тем как читать статью, нужно базово понимать некоторые термины:

  • Что такое RPC?

  • Что такое gRPC?

  • Что такое protobuf? Сюда же можно отнести знакомство с синтаксисом *.proto файлов;

  • Неплохо было бы знать/понимать синтаксис языка Go, хотя бы на базовом уровне;

  • Для запуска сервера через docker понадобятся базовые знания docker.

Без понимания выше описанного будет сложно разобраться о чем идет речь

Requirements

Для написания автотестов нам понадобится gRPC сервер. Поискал на просторах интернета открытые gRPC сервера, но ничего нашел. Поэтому придется написать свой сервер, который можно будет запустить локально и уже на него писать автотесты.

Исходный код сервера расположен на моем github. Инструкции по установке и настройке сервера прилагаются. Если у вас нет необходимости как-то изменять/расширять имеющийся контракт, то можно сразу пропустить секцию Setup protobuf

Запустить сервер можно двумя способами:

  • По этой инструкции установить все необходимые зависимости на вашу OS и запустить сервер;

  • Либо запустить сервер внутри docker, тогда нужно посмотреть эту инструкцию

После запуска сервера локально он будет доступен по адресу localhost:8000, ну или 127.0.0.1:8000. До написания автотестов неплохо было бы иметь клиент, с помощью которого можно будет «потыкать» сервер. Таким клиентом может быть Postman, но только десктопный вариант, в браузере gRPC не может быть использован. Инструкции о том, как настроить Postman на работу c gRPC

Теперь посмотрим на структуру контракта, который находится в репозитории с сервером. Этот же контракт мы будем использовать в автотестах

syntax = "proto3"; option go_package = "./;articlesservice";  service ArticlesService {   rpc GetArticle (GetArticleRequest) returns (GetArticleResponse);   rpc CreateArticle(CreateArticleRequest) returns (CreateArticleResponse);   rpc UpdateArticle(UpdateArticleRequest) returns (UpdateArticleResponse);   rpc DeleteArticle(DeleteArticleRequest) returns (DeleteArticleResponse); }  message Article {   string id = 1;   string title = 2;   string author = 3;   string description = 4; }  enum ErrorType {   NOT_FOUND = 0;   ALREADY_EXISTS = 1;   UNSPECIFIED = 2; }  message Error {   string message = 1;   ErrorType type = 2; }  message GetArticleRequest {   string article_id = 1; }  message GetArticleResponse {   oneof result {     Error error = 1;     Article article = 2;   } }  message CreateArticleRequest {   Article article = 1; }  message CreateArticleResponse {   oneof result {     Error error = 1;     Article article = 2;   } }  message UpdateArticleRequest {   Article article = 1; }  message UpdateArticleResponse {   oneof result {     Error error = 1;     Article article = 2;   } }  message DeleteArticleRequest {   string article_id = 1; }  message DeleteArticleResponse {}

В контракте описаны методы:

Также, обратите внимание, что есть модель Article, которая используется в методах создания, обновления и получения статьи. Эта модель специально вынесена отдельно, чтобы лишний раз не дублировать код

Выше приведен очень простой контракт на стандартные CRUD операции. На реальных проектах контракты могут быть намного сложнее, но для тестов нам подойдет и такой

Сторонние библиотеки, которые понадобятся:

  • grpc-go — для реализации gRPC клиента;

  • allure-go — для Allure отчета;

  • yaml — для чтения yaml файлов;

  • gomega — для проверок;

  • zap — для логирования;

  • dig — для реализации dependency injection;

  • sample_go_grpc_server — это сервер, про который говорилось выше, нужен для контрактов.

Есть еще ряд библиотек без которых никуда, но все они уже встроены в go

Configuration

Перед написанием автотестов нам необходимо добавить файл с конфигурациями, в котором опишем основные настройки

infrastructure/config-local.yml

articlesService:   port: 8000   host: localhost  logger:   isDevMode: true   level: debug

Напишем настройки для сервиса статей и логгера, скорее всего для вашего проекта понадобится больше конфигов, можете добавить их в этот infrastructure/config-local.yml файл

Лайфхак. Если есть необходимость запускать автотесты сразу на нескольких окружениях, то для этого можно создать файлы настроек под каждое из окружений, как например в нашем случае:

  • config-dev.yml

  • config-local.yml

  • config-stable.yml

Далее в зависимости от переменной окружения ENV, будем брать нужный файл и загружать настройки. Подробнее разберем ниже, когда будем описывать чтение настроек в файле utils/config/internal.go

Напишем модели, внутрь которых будут «помещаться» конфиги из файла infrastructure/config-{env}.yml

utils/config/models.go

package config  type Env string type LogLevel string  const ( DebugLogLevel   LogLevel = "debug" InfoLogLevel    LogLevel = "info" WarningLogLevel LogLevel = "warning" ErrorLogLevel   LogLevel = "error" )  type Logger struct { Level     LogLevel `yaml:"level"` IsDevMode bool     `yaml:"isDevMode"` }  type GrpcService struct { Port               int32  `yaml:"port"` Host               string `yaml:"host"` InsecureSkipVerify bool   `yaml:"insecureSkipVerify"` }  type Config struct { Logger          Logger      `yaml:"logger" validate:"required"` Articlesservice GrpcService `yaml:"articlesService" validate:"required"` }

Теперь напишем парсер, который будет читать конфиги и «раскладывать» их по моделям из utils/config/models.go

utils/config/internal.go

package config  import ( "fmt" "gopkg.in/yaml.v3" "io/ioutil" "os" )  func getEnv() (Env, error) { env := os.Getenv("ENV")  if len(env) == 0 { return "", fmt.Errorf("cannot parse env variable") }  return Env(env), nil }  func readConfig(configPath string, config interface{}) error { if configPath == `` { return fmt.Errorf(`no config path`) }  configBytes, err := ioutil.ReadFile(configPath)  if err != nil { return err }  if err = yaml.Unmarshal(configBytes, config); err != nil { return err }  return nil }  func NewConfig() (Config, error) { var config Config  env, err := getEnv()  if err != nil { return Config{}, err }  err = readConfig(fmt.Sprintf("../infrastructure/config-%s.yml", env), &config)  if err != nil { return Config{}, err }  return config, nil }

Обратите внимание, что мы динамически подставляем окружение в путь до настроек "../infrastructure/config-%s.yml"

Service

Добавим методы для взаимодействия с нашим сервером, но перед этим необходимо сделать gRPC клиент

utils/services/grpc/client.go

package grpc  import ( "fmt" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" "sample_go_grpc_testing/utils/config" )  func GetGrpcClient(grpcService config.GrpcService) (*grpc.ClientConn, error) { address := fmt.Sprintf("%s:%d", grpcService.Host, grpcService.Port)  return grpc.Dial(address, grpc.WithTransportCredentials(insecure.NewCredentials())) }

Функция GetGrpcClient будет принимать настройки Host, Port, для инициализации клиента, создавать клиент и возвращать его

Теперь можно написать клиент для сервиса ArticlesService. Структура, которой будем придерживаться:

  • client.go — описываем клиент и билдер, который будет конструировать клиент;

  • api.go — будет описывать методы для взаимодействия с сервером. По сути это будет просто обертка, внутри которой накинем логи, проверки;

  • steps.go — добавляем к методам из api.go allure шаги, описание, параметры, все что связано с отчетом. Можете пропустить этот слой, если вам не нужны шаги отчета, либо сам отчет;

core/articlesservice/client.go

package articlesservice  import ( articlesservice "github.com/Nikita-Filonov/sample_go_grpc_server/gen/proto" "sample_go_grpc_testing/utils/config" "sample_go_grpc_testing/utils/logger" "sample_go_grpc_testing/utils/services/grpc" )  type Client struct { articlesservice.ArticlesServiceClient logger *logger.CtxLogger }  func NewClient(logger logger.Service, conf config.Config) (*Client, error) { conn, err := grpc.GetGrpcClient(conf.Articlesservice)  if err != nil { return &Client{}, err }  return &Client{ logger:                logger.NewPrefix("ARTICLES_SERVICE.GRPC.CLIENT"), ArticlesServiceClient: articlesservice.NewArticlesServiceClient(conn), }, nil }

core/articlesservice/api.go

package articlesservice  import ( "context" articlesservice "github.com/Nikita-Filonov/sample_go_grpc_server/gen/proto" "github.com/onsi/gomega" )  func (c *Client) getArticle( g gomega.Gomega, ctx context.Context, request *articlesservice.GetArticleRequest, ) *articlesservice.GetArticleResponse { c.logger.InfofJSON("GetArticleRequest", request)  res, err := c.ArticlesServiceClient.GetArticle(ctx, request) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "GetArticle error")  c.logger.InfofJSON("GetArticleResponse", res) return res }  func (c *Client) createArticle( g gomega.Gomega, ctx context.Context, request *articlesservice.CreateArticleRequest, ) *articlesservice.CreateArticleResponse { c.logger.InfofJSON("CreateArticleRequest", request)  res, err := c.ArticlesServiceClient.CreateArticle(ctx, request) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "CreateArticle error")  c.logger.InfofJSON("CreateArticleResponse", res) return res }  func (c *Client) updateArticle( g gomega.Gomega, ctx context.Context, request *articlesservice.UpdateArticleRequest, ) *articlesservice.UpdateArticleResponse { c.logger.InfofJSON("UpdateArticleRequest", request)  res, err := c.ArticlesServiceClient.UpdateArticle(ctx, request) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "UpdateArticle error")  c.logger.InfofJSON("UpdateArticleResponse", res) return res }  func (c *Client) deleteArticle( g gomega.Gomega, ctx context.Context, request *articlesservice.DeleteArticleRequest, ) *articlesservice.DeleteArticleResponse { c.logger.InfofJSON("DeleteArticleRequest", request)  res, err := c.ArticlesServiceClient.DeleteArticle(ctx, request) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "DeleteArticle error")  c.logger.InfofJSON("DeleteArticleResponse", res) return res }

Из чего состоит каждый метод:

  • Логирование запроса, который отправляется на сервер;

  • Вызов удаленной процедуры;

  • Обработка ошибки с помощью gomega. В случае если сервер ответил ошибкой, то тест упадет уже на этом этапе. Если вам наоборот нужно вызвать ошибку, то можно написать другой метод и добавить проверку на то, что была получена ошибка g.Expect(err).Should(gomega.HaveOccurred(), "GetArticle error");

  • Если все хорошо и ошибки не случилось, то логируем ответ от сервера.

core/articlesservice/steps.go

package articlesservice  import ( "context" articlesservice "github.com/Nikita-Filonov/sample_go_grpc_server/gen/proto" "github.com/dailymotion/allure-go" "github.com/onsi/gomega" )  func (c *Client) GetArticle( g gomega.Gomega, request *articlesservice.GetArticleRequest, ) (response *articlesservice.GetArticleResponse) { allure.Step(allure.Description("Send ArticlesService.GetArticle request"), allure.Action(func() { response = c.getArticle(g, context.Background(), request) }))  return response }  func (c *Client) CreateArticle( g gomega.Gomega, request *articlesservice.CreateArticleRequest, ) (response *articlesservice.CreateArticleResponse) { allure.Step(allure.Description("Send ArticlesService.CreateArticle request"), allure.Action(func() { response = c.createArticle(g, context.Background(), request) }))  return response }  func (c *Client) UpdateArticle( g gomega.Gomega, request *articlesservice.UpdateArticleRequest, ) (response *articlesservice.UpdateArticleResponse) { allure.Step(allure.Description("Send ArticlesService.UpdateArticle request"), allure.Action(func() { response = c.updateArticle(g, context.Background(), request) }))  return response }  func (c *Client) DeleteArticle( g gomega.Gomega, request *articlesservice.DeleteArticleRequest, ) (response *articlesservice.DeleteArticleResponse) { allure.Step(allure.Description("Send ArticlesService.DeleteArticle request"), allure.Action(func() { response = c.deleteArticle(g, context.Background(), request) }))  return response }

Отлично, шаги написали, теперь стоит добавить утилиты, которые понадобятся для написания тестов

Logger

Логирование очень важный атрибут, без него будет сложно понять, что сейчас делает тест, на каком именно шаге он упал, что ответил сервер

В качестве библиотеки для логирования будем использовать zap

Напишем конфиги для логгера и билдер

package logger  import ( "sample_go_grpc_testing/utils/config" "time"  "go.uber.org/zap" "go.uber.org/zap/zapcore" )  var MapConfLevelZapLevel = map[config.LogLevel]zapcore.Level{ config.DebugLogLevel:   zap.DebugLevel, config.InfoLogLevel:    zap.InfoLevel, config.WarningLogLevel: zap.WarnLevel, config.ErrorLogLevel:   zap.ErrorLevel, }  type Service interface { NewPrefix(prefix string) *CtxLogger }  type loggerService struct { *zap.Logger }  func (ls *loggerService) NewPrefix(prefix string) *CtxLogger { return &CtxLogger{ls.Logger.Named(prefix)} }  func NewLoggerService(conf config.Config) (Service, error) { cfg := newConfig(conf.Logger)  zapLogger, err := cfg.Build() if err != nil { return nil, err }  return &loggerService{zapLogger}, err }  func utcTimeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) { enc.AppendString(t.UTC().Format("2006-01-02T15:04:05.000Z0700")) }  func newEncoderConfig() zapcore.EncoderConfig { return zapcore.EncoderConfig{ TimeKey:        "@timestamp", LevelKey:       "level", NameKey:        "logger_name", CallerKey:      "caller_file", MessageKey:     "message", StacktraceKey:  "stacktrace", LineEnding:     zapcore.DefaultLineEnding, EncodeLevel:    zapcore.CapitalLevelEncoder, EncodeTime:     utcTimeEncoder, EncodeDuration: zapcore.StringDurationEncoder, EncodeCaller:   zapcore.ShortCallerEncoder, } }  func newConfig(conf config.Logger) zap.Config { cfg := zap.Config{ Level:             zap.NewAtomicLevelAt(MapConfLevelZapLevel[conf.Level]), Development:       false, DisableCaller:     false, DisableStacktrace: false, Sampling: &zap.SamplingConfig{ Initial:    100, Thereafter: 100, }, Encoding:         "json", EncoderConfig:    newEncoderConfig(), OutputPaths:      []string{"stdout"}, ErrorOutputPaths: []string{"stdout"}, }  if conf.IsDevMode { cfg.Development = true cfg.Encoding = "console" }  return cfg }
  • utcTimeEncoder, newEncoderConfig, newConfig — по сути это просто утилиты, которые помогают настроить логгер

  • NewLoggerService — будет создавать и настраивать сервис логгера

  • NewPrefix — будет использоваться для добавления логгера к конкретному сервису, как мы это делали чуть ранее

Components/dependency injection

Будем использовать библиотеку dig для реализации dependency injection

Что такое dependency injection? Про это подробнее можно почитать например тут или тут

Зачем нам это нужно? Давайте разберем на примере автомобиля. Автомобиль состоит из колес, руля, двигателя, электроники, бензобака и т.д. Но конечного клиента, то есть водителя, не волнует, как, где, кем и когда были добавлены/созданы эти детали, водителю нужен лишь интерфейс управления машиной/двигателем. Водителю важен лишь конечный результат — в машину можно сесть, завести и поехать. Аналогично и с автотестам, для них требуется много сервисов/клиентов, например, базы данных, внутренние или внешние сервисы, рестовые апишки, вольт с кредами и т.д. По сути внутри теста не важно, кто, где и как инициализировал этих клиентов, нужен лишь интерфейс, чтобы мы могли пойти в базу данных, отправить запрос на сервер, получить какие-то креды и т.д.

Опишем компоненты, которые потом будем использовать в тестах

utils/common/container/components.go

package container  import ( "go.uber.org/dig" "sample_go_grpc_testing/core/articlesservice" "sample_go_grpc_testing/utils/config" "sample_go_grpc_testing/utils/logger" )  type Components struct { ArticlesService *articlesservice.Client  Logger logger.Service Config config.Config }  func initComponents(c *dig.Container) (*Components, error) { var err error components := Components{}  err = c.Invoke(func( articlesService *articlesservice.Client,  logger logger.Service, conf config.Config, ) { components.Config = conf components.Logger = logger  components.ArticlesService = articlesService })  if err != nil { return nil, err }  return &components, nil }

Теперь напишем билдер, который будет инициализировать компоненты

utils/common/container/api.go

package container  import ( "go.uber.org/dig" "sample_go_grpc_testing/core/articlesservice" "sample_go_grpc_testing/utils/config" "sample_go_grpc_testing/utils/logger" )  func BuildContainer() (*Components, error) { c := dig.New() servicesConstructors := []interface{}{ articlesservice.NewClient, config.NewConfig, logger.NewLoggerService, }  for _, service := range servicesConstructors { err := c.Provide(service) if err != nil { return nil, err } } components, componentsError := initComponents(c)  if componentsError != nil { return nil, componentsError }  return components, nil }

Подробнее про использование библиотеки dig вы можете почитать тут

Assertions

Для проверок будем использовать библиотеку gomega. Полную документацию можно почитать тут, лишь кратко скажу, что данная библиотека закрывает все базовые потребности для проверок:

У gomega еще есть много крутых фичей и настроек, но нам будет достаточно вышеперечисленного

Для начала напишем gomega инициализатор, который будет инициализировать gomega и добавлять базовые настройки

utils/common/gomega.go

package common  import ( "github.com/dailymotion/allure-go" "github.com/onsi/gomega" "github.com/pkg/errors" "runtime/debug" "testing" "time" )  func GetGomega(t *testing.T) gomega.Gomega { g := gomega.NewWithT(t) g.SetDefaultEventuallyTimeout(time.Second * 20) g.SetDefaultEventuallyPollingInterval(time.Second * 3) g.ConfigureWithFailHandler(func(message string, callerSkip ...int) { g.THelper() allure.Fail(errors.New(message)) t.Fatalf("\n%s %s", message, debug.Stack()) }) g.THelper = t.Helper return g }

Теперь нужно описать базовые проверки, которые будут использоваться во всем проекте

utils/assertions/common/solutions.go

package solutions  import ( "fmt" "github.com/dailymotion/allure-go" "github.com/onsi/gomega" )  func AssertToEqual(g gomega.Gomega, actual interface{}, expected interface{}, description string) { step := fmt.Sprintf("Checking that '%s' equals to '%s'", description, PrettifyValue(expected))  allure.Step(allure.Description(step), allure.Action(func() { g.Expect(actual).To(gomega.Equal(expected), step) })) }

Нам понадобится только одна проверка AssertToEqual, но в вашем проекте скорее всего понадобится намного больше проверок по типу AssertIsNotNil, AssertIsNil, AssertToHaveLen и т.д. Их также можно будет объявить в этом файле и далее использовать по всему проекту.

Лайфхак. Обратите внимание на то, каким образом значение expected подставляется в шаблон шага "Checking that '%s' equals to '%s'". Функция PrettifyValue специально используется, чтобы придать значению expected читабельный вид. Если использовать общие проверки для разных типов данных, то такой "Checking that '%s' equals to '%s'" шаблон форматирования будет нормально работать только для строк. Также в go есть поинторы, которые тоже будут отображаться некорректно. Конечно же вы можете написать проверку под каждый тип, например AssertToEqualString, AssertToEqualInt, AssertToEqualFloat и т.д., но тогда придется писать очень много дублированных проверок и под каждую нужно будет делать свой шаблон. В нашем случае функция PrettifyValue получает на вход любое значение и возвращает уже отформатированный, человеко-читабельный вариант в формате type[value], например string[MyString], int8[4], int32[1234] и т.д. Вы можете использовать другое решение, это лишь пример, как можно не дублируя проверки, сделать красивые шаги в отчете

Теперь напишем конкретные проверки уже для сервиса ArticlesService, а именно для модели Article

utils/assertions/articlesservice/articles.go

package articlesservicechecks  import ( "fmt" articlesservice "github.com/Nikita-Filonov/sample_go_grpc_server/gen/proto" "github.com/dailymotion/allure-go" "github.com/onsi/gomega" solutions "sample_go_grpc_testing/utils/assertions/common" )  func CheckArticle(g gomega.Gomega, actualArticle, expectedArticle *articlesservice.Article) { allure.Step(allure.Description("Checking article"), allure.Action(func() { solutions.AssertToEqual(g, actualArticle.Id, expectedArticle.Id, "Article Id") solutions.AssertToEqual(g, actualArticle.Title, expectedArticle.Title, "Article Title") solutions.AssertToEqual(g, actualArticle.Author, expectedArticle.Author, "Article Author") solutions.AssertToEqual(g, actualArticle.Description, expectedArticle.Description, "Article Description") })) }  func CheckArticleError(g gomega.Gomega, actualError, expectedError *articlesservice.Error) { allure.Step(allure.Description("Checking article error"), allure.Action(func() { solutions.AssertToEqual(g, actualError.Type, expectedError.Type, "Error Type") solutions.AssertToEqual(g, actualError.Message, expectedError.Message, "Error Message") })) }  func CheckArticleNotFoundError(g gomega.Gomega, actualError *articlesservice.Error, articleId string) { expectedError := articlesservice.Error{ Type:    articlesservice.ErrorType_NOT_FOUND, Message: fmt.Sprintf("Article with Id %s not found", articleId), } CheckArticleError(g, actualError, &expectedError) }

Лайфхак. Если вы пишете тесты для gRPC сервиса, то у вас наверняка есть много похожих моделей в проекте, которые используются в разных сервисах или могут быть вложены в другие модели. Для общих моделей сразу стоит делать отдельные проверки, например есть модель User, тогда делаем проверку CheckUser и используем ее внутри других проверок, например так:

...  func CheckAccount(g gomega.Gomega, actualAccount, expectedAccount *accountservice.Account) { allure.Step(allure.Description("Checking account"), allure.Action(func() { ...         CheckUser(g, actualAccount.User, expectedAccount.User)         ... })) }  ...

Это сэкономит больше времени в будущем, при написании новых проверок или же если нужно будет вносить изменения в уже имеющиеся проверки

Utils

Теперь почти все готово к написанию автотестов, добавим лишь несколько утилит, которые помогут в написании тестов

Напишем функцию GetRandomArticle, которая будет возвращать модель Article заполненную рандомными данными. Реализацию функции RandomString можно посмотреть тут

utils/controllers/articlesservice/articles.go

package articlesservicecontrollers  import ( articlesservice "github.com/Nikita-Filonov/sample_go_grpc_server/gen/proto" "github.com/google/uuid" "sample_go_grpc_testing/utils/fakers" )  func GetRandomArticle() *articlesservice.Article { return &articlesservice.Article{ Id:          uuid.NewString(), Title:       fakers.RandomString(30), Author:      fakers.RandomString(20), Description: fakers.RandomString(100), } }

Добавим функцию, которая будет подготавливать нужные для теста компоненты, в нашем случае components, gomega

utils/common/setup.go

package common  import ( "fmt" "github.com/onsi/gomega" "go.uber.org/dig" "sample_go_grpc_testing/utils/common/container" "testing" )  func SetupTesting(t *testing.T) (*container.Components, gomega.Gomega) { g := GetGomega(t)  c, err := container.BuildContainer() g.Expect(err).ShouldNot(gomega.HaveOccurred(), fmt.Sprintf("unable to build container: %v", dig.RootCause(err)))  return c, g }

Также вынесем все части касающиеся отчета в отдельный пакет, чтобы потом их можно было использовать в других автотестах

Testing

Теперь можно писать автотесты. Всего у сервиса ArticlesService, четыре метода, значит напишем четыре простых позитивных теста

tests/articles_test.go

package tests  import ( articlesservice "github.com/Nikita-Filonov/sample_go_grpc_server/gen/proto" "github.com/dailymotion/allure-go" "github.com/dailymotion/allure-go/severity" articlesservicechecks "sample_go_grpc_testing/utils/assertions/articlesservice" "sample_go_grpc_testing/utils/common" articlesservicecontrollers "sample_go_grpc_testing/utils/controllers/articlesservice" "sample_go_grpc_testing/utils/reports" "testing" )  func TestGetArticle(t *testing.T) { t.Parallel()  allure.Test(t, reports.ArticlesServiceFeature, reports.ArticlesSuite, allure.Severity(severity.Critical), allure.Tags(reports.ArticlesTag), allure.Name("Get article"), allure.Action(func() { c, g := common.SetupTesting(t)  article := articlesservicecontrollers.GetRandomArticle() c.ArticlesService.CreateArticle(g, &articlesservice.CreateArticleRequest{Article: article})  response := c.ArticlesService.GetArticle(g, &articlesservice.GetArticleRequest{ArticleId: article.Id})  articlesservicechecks.CheckArticle(g, response.GetArticle(), article) }), ) }  func TestCreateArticle(t *testing.T) { t.Parallel()  allure.Test(t, reports.ArticlesServiceFeature, reports.ArticlesSuite, allure.Severity(severity.Critical), allure.Tags(reports.ArticlesTag), allure.Name("Create article"), allure.Action(func() { c, g := common.SetupTesting(t)  article := articlesservicecontrollers.GetRandomArticle() response := c.ArticlesService.CreateArticle(g, &articlesservice.CreateArticleRequest{Article: article})  articlesservicechecks.CheckArticle(g, response.GetArticle(), article) }), ) }  func TestUpdateArticle(t *testing.T) { t.Parallel()  allure.Test(t, reports.ArticlesServiceFeature, reports.ArticlesSuite, allure.Severity(severity.Critical), allure.Tags(reports.ArticlesTag), allure.Name("Update article"), allure.Action(func() { c, g := common.SetupTesting(t)  article := articlesservicecontrollers.GetRandomArticle() c.ArticlesService.CreateArticle(g, &articlesservice.CreateArticleRequest{Article: article})  response := c.ArticlesService.UpdateArticle(g, &articlesservice.UpdateArticleRequest{Article: article})  articlesservicechecks.CheckArticle(g, response.GetArticle(), article) }), ) }  func TestDeleteArticle(t *testing.T) { t.Parallel()  allure.Test(t, reports.ArticlesServiceFeature, reports.ArticlesSuite, allure.Severity(severity.Critical), allure.Tags(reports.ArticlesTag), allure.Name("Delete article"), allure.Action(func() { c, g := common.SetupTesting(t)  article := articlesservicecontrollers.GetRandomArticle() c.ArticlesService.CreateArticle(g, &articlesservice.CreateArticleRequest{Article: article})  c.ArticlesService.DeleteArticle(g, &articlesservice.DeleteArticleRequest{ArticleId: article.Id})  response := c.ArticlesService.GetArticle(g, &articlesservice.GetArticleRequest{ArticleId: article.Id})  articlesservicechecks.CheckArticleNotFoundError(g, response.GetError(), article.Id) }), ) }

Report

Перед запуском тестов убедитесь, что сервер запущен и работает

Запустим тесты и посмотрим на отчет:

make test

Теперь запустим отчет:

allure serve

Либо можете собрать отчет и в папке allure-reports открыть файл index.html:

allure generate

Полную версию отчета посмотрите тут.

Заключение

Весь исходный код проекта с автотестами расположен на моем github.

Весь исходный код проекта с сервером расположен на моем github.


ссылка на оригинал статьи https://habr.com/ru/articles/736502/

Самые громкие события инфобеза за май 2023 года

Всем привет! Мы подводим итоги последнего весеннего месяца в нашем традиционном дайджесте. В мае особо отличилась Toyota, обнаружившая у себя несколько облаков, висевших без пароля десять лет, злоумышленники из Cl0p устроили массовую атаку через софт для передачи файлов MOVEit, вновь всплыла спайварь Pegasus – на этот раз в зоне военного конфликта, разгорелись дискуссии вокруг нового zip-домена от Google… Об этом и других интересных инфобез-событиях мая читайте под катом!

Взлом софта для передачи файлов MOVEit от Cl0p

В последние дни мая произошла череда атак через нулевой день на эскалацию привилегий и неавторизированный доступ в в софте для передачи файлов MOVEit. Злоумышленники потенциально взломали сотни организаций и украли всевозможные конфиденциальные данные. В списке взломов уже засветились такие крупные компании, как BBC, British Airways, Aer Lingus и другие в Британии, США и Канаде. Они пока заявили об утёкших домашних адресах, номерах национальных страховок, размерах зарплат, а в некоторых случаях и банковских реквизитах более 100 тысяч сотрудников.

Microsoft вскоре приписала атаку группировке Cl0p, которая подтвердила свою причастность к взломам. Злоумышленники утверждают, что взломали сотни компаний и выдвинули им ультиматум до 14 июня. Что любопытно, в этот раз Cl0p не шантажирует пострадавших напрямую, а требует выйти с ними на связь для обсуждения выкупа самим. С чем связана смена тактики, неясно. Возможно, у группировки в рукаве действительно козырь на множество взломов, которые им лень обрабатывать лично. Пока же серьёзность заявлений группировки ещё только предстоит оценить, когда начнутся сливы украденных данных. Так или иначе, этот масштабный взлом, судя по всему, снова забросит клопов-оппортунистов на верхушку рансомварь-иерархии текущего сезона, обойдя Lockbit и других их конкурентов на этом киберпреступном поприще.

Вишенкой же на торте взломов стало то, что исследователи обнаружили следы активности группировки по этому нулевому дню аж в июле 2021-го года. Логи взломанных компаний показывают, что злоумышленники на протяжение двух лет несколько раз тестировали уязвимость и свои инструменты для атаки.

В июле 2021-го Cl0p стучались командами на сервера вручную – видимо, примерно тогда был обнаружен задел под атаку. А в апреле 2022-го года по серверам массово прошлись уже с автоматизацией, собирая информацию о компаниях, к которым был доступ. Полировка клоповьих инструментов заняла ещё год перед их звёздным часом в конце мая. Собственно, перед нами наглядный пример того, что рансомварщик – это порою довольно развитая форма жизни, способная не только на поиск мгновенного вознаграждения, но и на многолетнюю работу на результат.

 Toyota витает в облаках

В мае Toyota сообщила об утечке данных из своего облака, из-за которой была доступна информация о локации их машин. Потенциальной утечкой были затронуты 2,150,000 автовладельцев. А причиной тому была криво настроенная база данных, доступ к которой мог получить любой желающий без пароля. На протяжение 10 лет с ноября 2013-го по апрель 2023-го года.

Утечка затронула владельцев авто, которые пользовались сервисами T-Connect G-Link, G-Link Lite и G-BOOK. В открытом доступе был ID GPS-навигатора, номер шасси и данные по местонахождению авто с привязкой по времени. Во втором заявлении Toyota также сообщила, что мог быть доступ к видео с авто в течение семи лет. Информации о том, что эти данные были кем-либо использованы, нет. И злоумышленнику также нужно было бы также знать номер шасси авто для его отслеживания – то есть иметь физический доступ к машине. Тем не менее, Toyota пообещала принести персональные извинения всем затронутым автовладельцам.

Однако на этом история не закончилась. По следам конфуза Toyota провела аудит своих серверов. И что бы вы думали, нашла ещё два неверно настроенных сервера, с которых утекала личная информация владельцев автомобилей их брендов. В этот раз на протяжение семи лет.

На одном сервере для автодилеров и сервисов лежала информация клиентов из Азиатско-Тихоокеанского региона: адреса, имена, телефоны, почты, заводские номера и номера машин. Сколько клиентов затронуты этой утечкой, компания не сообщает. На втором сервере была менее чувствительная информация с навигаторов владельцев лексусов из Японии, которая из базы регулярно удалялась. Так что здесь назвать число задетых не постеснялись — 260 тысяч счастливчиков. Как утверждают в Toyota, они запустили систему для мониторинга конфигов своих облачных серверов и баз, чтобы предотвратить подобные утечки в будущем. Почему такой системы в виде компетентных сотрудников не было раньше, в компании не уточнили.

Спайварь Pegasus в зоне военного конфликта

В конце мая вышел отчёт об использовании спайвари Pegasus в Армении с октября 2020-го по декабрь 2022-го. На этот раз оперирующая Pegasus израильская компания отличилась новым пробитым дном. Спайварь нашли на устройствах журналистов, правозащитников и политиков, часть из которых предпочли сохранить анонимность в целях безопасности. А активность совпала с пиками обострений и переговорами во время армяно-азербайджанского конфликта. Иными словами, перед нами, видимо, первое применение Pegasus в зоне военного конфликта

Косвенные данные свидетельствуют о том, что заказчиком был Азербайджан. При этом исследователи допускают, что армянское правительство тоже заинтересовано в слежке за правозащитниками, но у него под это дело, судя по всему, подписка только на другую – что иронично, тоже израильскую – спайварь Predator, а заказчиков под Pegasus в отличие от Азербайждана в Армении не находили. В общем, товарищи из NSO Group продолжают игнорировать международное гуманитарное право и дошли до того, что обслуживают интересы правительств в военных конфликтах. По крайней мере, их рекламные буклеты не врут: «национальную безопасность» они действительно укрепляют. В этот раз азербайджанскую.

К вопросу об упомянутой выше спайвари Predator от израильской компании Intellexa. По ней в мае также вышло исследование, в этом случае с детальным разбором функционала. Спайварь записывает звонки, собирает сообщения с мессенджеров, может скрывать приложения на заражённом телефоне и препятствовать их запуску. Плюс в ней выполнение произвольного кода, нумерация каталогов и кастомные сертификаты на уровне пользователя.

Predator идёт со своим загрузчиком Alien. Что отличает его от копеечных инфостилеров, так это обход защиты SELinix. Alien также работает на ioctl-командах, которые SELinux не проверяет, и спайварь стягивает данные с устройства без нарушения прав доступа. Неизученными остались два модуля спайвари. Исследователи считают, что в них зарыты отслеживание геолокации, угон камеры телефона под произвольные фото и чтение/запись kernel space. Подробнее о Predator в отчёте от Cisco Talos.

Простор для махинаций на новом zip-домене

В ушедшем месяце запущенные Гуглом новые домены верхнего уровня разожгли горячие дискуссии в инфобез-сообществе. А всё потому что среди них оказались .zip и .mov. Как считают некоторые специалисты, это создаёт ненужные риски и простор для фишинговых атак. На фоне этого на zip-домене регистрируют образовательные страницы на тему того, почему .zip-домена не должно быть. А также запрашивают удаление новых доменов из Public Suffix List Мозиллы, демонстрируют потенциал фишинговых страниц под видом ссылок на легитимные файлы с Гитхаба… В общем, страсти кипят.

Между тем пока часть безопасников посмеивается над «раздутой .zip-угрозой», злоумышленники тоже не дремлют. В считанные дни обнаружились фишинговые страницы, например, для кражи данных доступа Microsoft по чудесному адресу microsoft-office[.]zip. И гиперссылки, оканчивающиеся на .zip, которые теперь расползутся по сети, явно не поспособствуют безопасности среднего юзера. Так что ирония некоторых специалистов по информационной безопасности может быть преждевременной.

Тем временем не прошло и месяца с появления zip-домена от Гугла, как всплыли и более продвинутые примеры малвари на нём. Исследователь mr.d0x, автор нашумевшей атаки браузер-в-браузере, собрал фишинговый тулкит в виде нового фейкового окна в браузере, которое выглядит, как Winrar с открытым юзером zip-архивом. Для убедительности в окно вшита кнопка Scan, клик по которой, конечно, никаких угроз не выявляет.

Такой тулкит может быть с лёгкостью использован для кражи данных доступа и доставки малвари. Особенно если злоумышленник зарегистрирует соответствующий zip-домен, ссылку на который и Winrar-окно на нём средний юзер примет за чистую монету. Альтернативный пробный вариант от mr.d0x выглядит как фейковое окно Windows File Explorer с zip-файлом. Немного креатива с CSS и HTML, и такие окошки будут выглядеть ещё более убедительно. В общем, Гугл своим zip-доменом приоткрыл занятный ящик Пандоры. Будет неудивительно, если мы уже совсем скоро услышим про фишинговые кампании и громкие взломы с zip-душком.

Утёкшие ключи прошивки от MSI и Intel

В мае также подоспели первые последствия апрельского взлома MSI. Компания, напомню, пыталась сохранить лицо и отрицала какой-либо серьёзный ущерб своим системам. Подробностями атаки они тоже не делились. Между тем в сухом заявлении MSI по следам взлома было интересно другое: настойчивый призыв к пользователям ставить обновления BIOS/прошивки только с официального сайта. Так что было очевидно, что исходники компании действительно оказались в руках у злоумышленников. Сами же они утверждали что стянули 1,5TB документов, ключей и исходников, включая прошивку.

И вот в ушедшем месяце группировка выложила исходники в открытый доступ, и среди них оказались ключи для прошивки самой MSI (57 штук) и от Intel Boot Guard (166 штук). Утёкшие ключи могут быть использованы злоумышленниками для подписи малвари в UEFI-буткитах. Ключевая проблема в том, что ключи зашиты в интеловское железо и сменить их нельзя. Так что одним взломом Intel Boot Guard превращается в тыкву – скомпрометированы оказались модели на процессорах с 11 по 13 поколения.

Между тем исследователи ещё в процессе разбора слива и обнаружили в нём, например, интеловский OEM-ключ, которым подписаны устройства от HP, Lenovo, AOPEN, CompuLab… Иными словами, утечка потенциально затрагивает не только модели от самой MSI, но также и продукты компаний по всей индустрии. И её масштабы ещё только предстоит оценить. Так, например, некоторые исследователи высказывают опасения, что последствиями взлома могут стать масштабные атаки на цепочку поставок с участием подписанной ключами компании малвари, которым доверяют огромное количество скомпрометированных устройств.

Перехват доменов Try2Check и криптообменных площадок

Напоследок к вопросу о закрытых в мае киберпреступных площадках. Так, май стал последним месяцем для Try2Check. ФБР перехватило домены одного из старейших киберпреступных сервисов для проверки валидности украденных кредитных карт. Запущенный в 2005-м году сервис пользовался доверием многочисленных кардинговых площадок и обрабатывал больше миллиона запросов в месяц. Он принёс владельцу не меньше $18 миллионов, и им был наш соотечественник. Сорокатрёхлетний Денис Кульков из Самары.

В базе закрытой в 2017-м криптобиржи BTC-e, через которую киберпреступники отмывали миллиарды долларов, товарищ Nordex, управлявший Try2Check, нашёлся под реальным именем. Затем правоохранительные органы обнаружили его ICloud-аккаунт, получили к нему доступ через суд… А там у Кулькова лежали фото его паспорта, где-то использованные для регистрации. На том можно было и закончить, но Брайан Кребс по традиции нарыл на героя истории всю подноготную. Что иронично, во многом благодаря слитым российским базам.

В статье у Кребса явки, пароли, адреса, данные по регистрации авто – роскошной Ferrari со столичных улиц. Подробнее о Кулькове читайте по ссылке, ну а желающим помочь ему выехать в зону Европола Штаты предлагают уже традиционный в таких случаях бонус до 10 миллионов долларов.

И наконец, в прошлом месяце ФБР перехватило девять криптообменных площадок, отмывавших деньги для скамеров и прочих киберпреступников, включая рансомварь-группировки. Сервера находились в Штатах, Украине и нескольких других европейских странах. А сами сайты отличались околонулевой KYC-политикой и были рассчитаны на англо- и русскоязычную публику. Заглушка на сайтах на двух языках как бы намекает на целевую аудиторию.

Перехват площадок усложнит финансовые операции рансомварщикам, а сервера в руках у ФБР сулят дальнейшие аресты среди владельцев и клиентов отключённых сервисов. Полный список по ссылке. Между тем это уже не первый в этом году удар по теневым криптобменникам: в марте на дно ушёл ChipMixer, а в январе был перехвачен Bitzlato, и владельцам обоих грозят солидные сроки. Так что месседж для Дикого Криптовалютного Запада, забитого ландроматами разной степени сомнительности, вполне понятный. Услуг ex-Conti, LockBit и прочим замечательным людям лучше не оказывать.


ссылка на оригинал статьи https://habr.com/ru/companies/tomhunter/articles/741076/

Не Валенсией единой. Барахолка Барселоны

А вот и нет, это не Денис aka @DRoman0vи не блог компании Selectel!
Также это не начало конкурирующей серии статей. Я хочу разок поделиться своими впечатлениями от посещения другой барахолки в Испании.

Я с большим интересом читаю статьи Дениса разных вещах и технике с барахолки, потому не мог пройти мимо самого большого блошиного рынка Барселоны и старейшего рынка Европы — Els Encants. Хочу поделиться с вами тем, что увидел.

Els Encants, общий вид

Так как это не радиорынок, то компьютерной техники тут немного. А того, что я ждал увидеть больше всего — телефоны, коммуникаторы и КПК начала-середины 2000-х, тут вообще не было. Особенно хотел увидеть Nokia 3250 (угадайте, почему). Скорее всего, мне просто не повезло. Говорят, что тут очень быстрая сменяемость ассортимента, так что надо будет обязательно заглянуть сюда ещё раз.

Приходить сюда лучше с фиксированной суммой в кармане, потому что внутренний Плюшкин здесь вырывается на свободу и приобрести хочется сразу всё! Миниатюрные фарфоровые модельки машин, старые игрушечные роботы, коллекции машинок (которые приводили в восторг полуторагодовалого сына), игровые консоли, ради которых раньше я был бы готов навсегда отказаться от сладкого. Всего этого навалом за смешную цену здесь и сейчас!

Интересная техника

Так как я не знал рыночной стоимости некоторой техники, то не мог оценить, хорошую ли мне предлагают цену. Одно можно сказать точно — тут можно и нужно торговаться. Примеры будут ниже. Для того, чтобы вам при прочтении было более понятно, я буду указывать цену на барахолке и цену на похожие лоты на ebay.

Игровые приставки

Как уже говорилось, техники тут немного. Но всё-таки интересные экземпляры попадались:

Nintendo DS (если я правильно понимаю, самой первой ревизии, не lite). В полностью рабочем состоянии, хоть и с поцарапанным верхним экраном и без стилуса. Учитывая, что согласно Википедии их продано больше ста миллионов (!), то их на вторичном рынке должо быть как глази, при этом на Ebay самые ушатанные варианты начинаются от 35€! Тут она обошлась бы в 15€ без учёта возможного торга.

PSP FAT (фото нет, к сожалению). Также за 15€. Включается, не работает крестовина. Скорее всего что-то пролили. Когда-то давно на своей PSP столкнулся с таким. Помогло промывание контактов под водой. Возможно тут такой же случай, тогда 15€ нормальная цена.

PS 1. В комплекте только карта памяти. Состояние: неизвестно. Цена: Неизвестна

Аудиотехника

Диктофон AIWA TP-61R. Модель 1965 года! Когда продавцы видят интерес покупателя, сначала ломят космическую цену. Цены в интернете от 50 до 150€(если комплектация оригинальная). За этот аппарат в неизвестном состоянии просили фифтииииии файф (цену в 55€ он явно придумывал на ходу). Поняв, что это перебор, тут же уступил до 40. Последним, что я услышал уходя, было 20€. Короче, нужно торговаться!

Фототехника

Фотоаппаратов любых годов и исполнений тут больше всего. Совсем древние, плёночные, на картриджах, цифровые. Аккумуляторов внутри или нет, или они дохлые, так что работоспособность не проверить. Также много объективов.

Особенно было интересно подержать в руках Sony Mavica MVC-FD71 с дискетой 3.5″, хоть он и не включился. От цены в 35€ я отказался. Тогда продавец взял пакет, подошёл к мне и спросил мою цену. Когда я сказал, что за 10 готов, он закинул фотоаппарат в пакет и протянул мне. (надо было говорить 5).

Осторожно! Gif 5Mb

Другая причудливая камера от Sony. Судя по всему, для видеоблогеров. Включилась при повороте камеры

Аналог Polaroid, Fujifilm Instax 210. 50€. На ebay примерно так же. Картриджи увидел позже у другого продавца.

Докстанции

От совсем древних до вполне современных. Цены от 20 до 35€

Первый специализированный шахматный компьютер 1978го года Champion MK I

Продавец предложил 30€. Не уверен, что устройство рабочее, но цены на ebay, как оказалось, стартуют от 150€! Так что можно было бы и попробовать восстановить при возможности.

Из техники Apple тут повстречались

Apple TV 3 версии. Не знал, что они были и в белом исполнении тоже. По 20€, что похоже на цены таковых на Авито (однако даже YouTube там уже не работает, так что польза сомнительная. Разве что шарить экран айфона на телевизор).

iPod Shuffle разных версий россыпью. Без наушников

Один iPhone на весь рынок. Не включился.

Прочее

Множество IP камер

Чехлы россыпью. По факту на последние айфоны и на 5-6 самых распространённых здесь китайских телефонов.

Квадрокоптер. 50€. Новый от 170€.

Интересная не техника

Ракетка. Такая уже встречалась у Дениса в рубрике «Что это за штука?». Похоже действительно популярный способ хранить ракетки.

Пластинки хранятся правильно, не плашмя. Так что можно рассмотреть к приобретению. На фото видны полноразмерные LP на 33 оборота, а также маленькие на 45 оборотов, их можно отличить по бОльшему отверстию посередине (обычно там одна влазит песня на сторону).

Мультитулы. С детства имею к ним слабость, особенно миниатюрным, поэтому не мог пройти мимо. Маленькие от 3€, полноразмерные от 5€.

Вино. Интересно, есть способ проверить, реально ли оно 70х годов?

Ключи и шестерёнки в огромном количестве

Настоящие наручники. Неожиданно тяжелые

Электротранспорт для детей

Очень много различного ручного инструмента

Немного о безопасности

С безопасностью в Барселоне, скажем так, не очень хорошо. Речь не о том, что можно получить по голове, а о кражах, особенно в туристических местах. Постоянно слышу, как у кого-то из знакомых, или знакомых знакомых что-то украли, стащили и т.д.
Через 5-10 минут нахождения на этом рынке, приметил пару парней, которые ошивались около нас, куда бы мы не отходили. Однако быстро потеряли к нам интерес, видя, что все вещи мы держим при себе (туристы ведут себя по-другому, потому они для них более интересная цель).

Итог

Я пришёл сюда с семьёй и каждый из нас увидел для себя что-то интересное: и я, и жена, и даже маленький сын. Если бы я пришёл сюда один, точно застрял бы на целый день и ушёл с полным мешком всякого! Надеюсь вам тоже было интересно.

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Увидели что-то интересное?
91.67% Да, было интересно 11
8.33% Вот именно из-за таких статей Хабр больше не торт! 1
0% Свой вариант в комментариях 0
Проголосовали 12 пользователей. Воздержались 3 пользователя.

ссылка на оригинал статьи https://habr.com/ru/articles/738092/