-Поиск по дневнику

Поиск сообщений в rss_rss_hh_new

 -Подписка по e-mail

 

 -Статистика

Статистика LiveInternet.ru: показано количество хитов и посетителей
Создан: 17.03.2011
Записей:
Комментариев:
Написано: 51

Habrahabr/New








Добавить любой RSS - источник (включая журнал LiveJournal) в свою ленту друзей вы можете на странице синдикации.

Исходная информация - http://habrahabr.ru/rss/new/.
Данный дневник сформирован из открытого RSS-источника по адресу http://feeds.feedburner.com/xtmb/hh-new-full, и дополняется в соответствии с дополнением данного источника. Он может не соответствовать содержимому оригинальной страницы. Трансляция создана автоматически по запросу читателей этой RSS ленты.
По всем вопросам о работе данного сервиса обращаться со страницы контактной информации.

[Обновить трансляцию]

[Из песочницы] Мой опыт проведения курсов по Java для новых сотрудников

Воскресенье, 17 Сентября 2017 г. 14:51 + в цитатник
nzeshka сегодня в 14:51 Разработка

Мой опыт проведения курсов по Java для новых сотрудников

Всем привет!

Сегодня практика ведения курсов перед трудоустройством достаточно распространена во многих компаниях, я и сам когда-то начинал свою карьеру с прохождения обучения, но мне всегда было интересно попробовать себя в качестве преподавателя. И этой весной я узнал, что в нашей компании весной-летом пройдут курсы по итогам которых команды пополнятся бойцами по ряду направлений: Android, Java, Javascript и QA. По предложению руководителя отдела, мне и моим коллегам выпала возможность провести курсы по направлению Java. О деталях этого увлекательного мероприятия я и хотел бы рассказать дорогому Хабру.

Отбор кандидатов




Что требуется от потенциального сотрудника в нашем отделе? В кратце, мы используем достаточно типичный стэк технологий: Java 8, Spring Framework, Hibernate/MyBatis, PostgreSQL. Следовательно необходимо коснуться всех ключевых элементов перечисленных выше, но т.к. уровень соискателей будет невысоким, кому-то возможно впервые придется столкнуться с рядом наименований, поэтому курсы предполагались (так и вышло) достаточно плотными по своему содержанию. Единственно, нам не хотелось затрагивать совсем детские темы вроде типов данных, разъяснения ключевых слов class, interface, циклов, операторов ветвления, поэтому на первом этапе наши кадровые сотрудники организовали дистанционное тестирование с двумя простейшими задачами, которые должны были убедить нас в том, что человек хоть как-то умеет программировать. Заявок на наше направление Java пришло около 150, в основном студенты старших курсов или недавние выпускники. Встречались и совсем экзотические представители вроде 40-летних менеджеров-предпринимателей или студентов из Африки. При проверке вступительных заданий я уделял больше внимания тому, как человек относится к оформлению своей работы, а не к тому, насколько изощренно человек «проходит циклом по массиву». В задачах уровня Hello World разбег для этого не слишком большой и пытаясь выделываться можно встретить от проверяющего недоуменный вопрос «Зачем так сложно?». В общем, кто-то присылал скриншоты из среды разработки, кто-то вставлял куски кода в документ Word, а кто-то, что порадовало, присылал ссылку на github. За сим перейдем ко второму этапу, когда письма счастья с новостью о зачислении разлетелись по своим адресатам.

Программа курсов


Базовые темы мы обозначили как Java Core:

Занятие 1.

Введение. Краткий рассказ об истории языка, немного о работе JVM и кроссплатформенности, принципы ООП (три кита, SOLID, KISS, YAGNI) и инструменты современного Java разработчика (IDE, CI, VCS, багтрекер).

Комментарий: темы достаточно разноплановые, но они, по нашему мнению, дают неплохой «заход в тему». От себя добавлю, что часть про ООП достаточно трудна на первом занятии, но её наличие позволяет в дальнейшем ссылаться на неё при проверке домашних заданий или будущих лекциях.

Занятие 2.

Исключения, потоки ввода-вывода, сериализация.

Комментарий: здесь все темы неплохо дополняют друг друга, можно продемонстрировать закрытие потоков в «классическом стиле» в блоке finally и показать аналог с try-with-resources. Потоки-обёртки вроде DataInputStream и DataOutputStream являются простым и наглядным примером паттерна Декоратор, поэтому здесь появляется возможность для отсылки к первому занятию.

Занятие 3.

Коллекции.

Комментарий: все, кто хоть раз проходил техническое собеседование, знают, что без коллекций не обойтись, т.к. здесь лежит целая кладезь нюансов позволяющая определить уровень знаний специалиста на уровнях Junior-Middle-Senior. Отдельно от внутреннего устройства структур данных рассматриваются такие темы как: классическое сравнение ArrayList vs LinkedList, «ключевые» проблемы HashMap (как можно потерять значение, виды разрешения коллизий), виды итераторов (fast-fail vs fast-safe), спецколлекции вроде EnumSet. Во время лекции часто ссылаюсь на посты tarzan82, очень нравится наглядность и последовательность изложения.

Занятие 4.

Многопоточность.

Комментарий: данное занятие дает основы написания многопоточного кода, знакомит с наиболее популярными проблемами (livelock, deadlock, starvation), а также с условно «нижнеуровневым» (Thread, Runnable) и «верхнеуровневым» параллельным API (java.util.concurrent), с синхронизированными и потокобезопасными коллекциями.

Занятие 5.

Новшества Java 8.

Комментарий: так как на проектах активно используется Stream API, лямбда-выражения и функциональные интерфейсы, то есть необходимость рассказать о нововведениях и современных подходах по обработке данных в тех же коллекциях.

Занятие 6.

Подведение итогов Java Core блока.

Комментарий: в этот блок попадают Generics, Reflection API, Enum и все, что упоминалось, но не обсуждалось подробно на предыдущих занятиях. Формат больше в виде «вопрос-ответ».

Занятия 7 и 8.

Spring Framework и Hibernate.

Комментарий: краткий обзор в стиле Get Started с полезными ссылками и рекомендациями по изучению, особой глубины за 1 занятие достичь здесь не удастся при любом желании. Каждый фреймворк достоен отдельного курса.

Занятия 9-10.

SQL Basic.

Комментарий: в заключительных занятиях рассказываются основы написания SQL запросов, объясняются алгоритмы соединения (hashjoin, merge-sort join, nested loop), как писать запросы более эффективно.

Итог


Желание вести курсы выразили еще двое коллег, поэтому за право вести ту или иную тему мы тянули соломинку. Параллельно лекционным занятиям всем обучающимся выдавалось задание (desktop приложение и веб-приложение на базе Spring). По итогам курсов некоторых ребят мы рекомендовали к собеседованию, так у нас появилось три новых Junior Java Developer, которые успешно вкатываются в проект.

Дабы тема была до конца раскрыта, опишу ряд проблем, с которыми мы столкнулись:


1. Изначально группа насчитывала 18 человек, основное время обучения выпало на июнь, когда у студентов проходит сессия. С одной стороны время не самое лучшее — те, у кого есть проблемы с учебой не смогут уделять должного времени курсам, с другой — дополнительная нагрузка служила естественным фильтром от лентяев. Ведь где гарантия, что после трудоустройства проблемы с учебой не заставят сотрудника так же забивать на работу? Вопрос о времени проведения очень дискуссионный, от меня и других преподавателей это не зависело. Весь курс проходил в режиме 2 занятий в неделю.
2. Проверка домашних заданий проходила по ряду критериев:
  • Соответствует ли написанное тому, что есть в задании. При этом свобода реализации оставалась за самим студентом, т.е. описывается необходимый функционал и некоторый набор технологий, который рекомендуется применить. Как и что с чем использовать, студент должен разобраться самостоятельно. Естественно были бонусы за оригинальность.
  • Насколько хорошо студент понял объектно-ориентированный подход. Часто начинающие Java разработчики тянут из Pascal/C++ процедурный стиль выражающийся в обилии статических методов, все лишь бы не создавать объект.
  • Соответствие Java Code Conventions. Увы, но на первых порах приходится уделять этому много времени, иначе от чтения кода дергаются веки.
  • Срок сдачи. Здесь мы были достаточно лояльны, т.к. сами еще недавно были студентами, но тем, кто сдал раньше были преференции.

Студенты присылали задание на почту, далее я или мои коллеги писали небольшое сопроводительное письмо в ответ, в котором сообщали вердикт (принята работа или нет) и давали ряд комментариев по недочетам. Таких итераций на одно задание максимально доходило до 3, каких-то пределов мы не ставили. Было выяснено опытным путем, что даже если вы уделите особое внимание на лекции некоторой распространенной ошибке или покажете best practice, то все равно будет группа людей, которая это пропустит мимо. Таким образом, презентация по лекции становилась удобным справочником, в который можно тыкать носом отправлять провинившихся для просвещения.
3. Если судить по количеству вопросов, то наибольшую трудность вызывали параллельное программирование (максимальное количество) и ООП (минимальное — нет вопросов). С первым все понятно, тема очень глубокая и ей нужно заниматься отдельно. В голову попадает сразу большой объем информации, в которой очень много нюансов, сразу переварить не удается. Со вторым, интереснее, т.к. в большинстве своем принципы объектно-ориентированного программирования выражены в предложениях рекомендательного характера, на простом человеческом языке, без привязки к чему-то конкретному, то, по моему мнению, при первой встрече студенты не относятся к ней слишком серьезно и стараются дождаться материала с листингами кода, где по их мнению есть видимая практическая ценность. Правда как только доходит дело до практики — именно здесь наибольшее количество проблем.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/338104/


Метки:  

Как я проходил собеседования на позицию Junior .Net Developer

Воскресенье, 17 Сентября 2017 г. 14:21 + в цитатник
JosefDzeranov сегодня в 14:21 Разработка

Как я проходил собеседования на позицию Junior .Net Developer

    Приветствую всех. Сегодня расскажу вам как я проходил собеседования в Москве на позицию .Net Developer. Усиленно готовился к собеседованиям месяц, целыми днями сидел и смотрел вопросы и пытался отвечать на них, а также читал книжки по С#. В статье привожу интересные задачки и вопросы, которые мне задавали в разных компаниях в Москве. Заранее скажу, что я попал в ту компанию, в которой хотел. Я прошел 4 собеседования в этой компании и меня наконец то взяли! Много статей было прочитано в частности здесь и надеюсь, что эту статью тоже будут читать начинающие Net разработчики и спрашивать все в комментариях.Кому интересна тема прохождения собеседований, прошу под кат! При поиске работы я пользовался Head Hunter. Там очень много вакансий с подробным изложением того, что хотят фирмы от кандидата. Решил откликнутся практически на все вакансии «C# разработчик», потому что для меня главное было пройти как можно больше собеседований, набраться опыта, а также во время таких собеседованиях найти интересную работу в интересной компании (забегая вперед скажу, что я нашел очень интересную работу). Некоторые компании (всего 2), для того, чтобы откликнуться на предложенную ими вакансию, хотели, чтобы кандидат решил предложенные ими задачи (напоминаю это при отклике). На мой взгляд это даже правильно, ведь это показывает, что компания очень ценит свое время и хочет, чтобы кандидат на собеседовании умел решать простые задачи. Обычно на таких собеседованиях спрашивают более «узкие» вопросы. Практически во всех компаниях есть несколько этапов собеседований. На первом обычно собеседует hr, на второй — старший разработчик, на третьем — технический директор. Бывали компании, в которых на первом же приходили и старший разработчик, и Team Lead и технический директор, что конечно чуть заставляет нервничать. Пройдя 5 собеседований, я выяснил такую вещь: чем позитивнее и свободнее ведешь себя на собеседовании, тем легче и быстрее она проходит.А теперь хочу поделиться с вами тем, какие вопросы мне задавали на очных собеседованиях. Поехали.Я не ожидал так много вопросов по базам данных, мне пришлось быстро осваивать (точнее вспоминать) sql язык. Вот какие вопросы и задания мне задавали:
    1. Что такое кластеризованный и некластеризованный индекс? Когда какое надо использовать?
    2. Что такое Join? Чем он отличается от Left Join, Right Join? Inner Join? Outer Join?
    3. Есть три таблицы: CUSTOMERS (ID, NAME, MANAGER_ID); MANAGERS (ID, NAME); ORDERS (ID, DATE, AMOUNT, CUSTOMER_ID). Написать запрос, который выведет имена Customers и их SalesManagers, которые сделали покупок на общую сумму больше 10000 с 01.01.2013.
    4. Делаем электронный справочник по книгам. Ищем:А) В каком магазине купить данную книгу.Б) В каких магазинах купить книги этого автора (авторов)В) Кто автор книгиГ) Какие книги написал авторНарисовать БД. Написать запрос Б. (Не забыть учесть, что у одной книжки — может быть несколько авторов)
    5. Что такое агрегирующие функции? Операторы Group By, Having? Приведите примеры их использования.
    6. Table «PC» (id, cpu(MHz), memory(Mb), hdd(Gb))1) Тактовые частоты CPU тех компьютеров, у которых объем памяти 3000 Мб. Вывод: id, cpu, memory2) Минимальный объём жесткого диска, установленного в компьютере на складе. Вывод: hdd3) Количество компьютеров с минимальным объемом жесткого диска, доступного на складе. Вывод: count, hdd
    7. Дана следующая структура базы данных в MS SQL: Departments (Id, Name), Employees(Id, DepartmentId, Name, Salary)Необходимо:• Написать запрос получения имени одного сотрудника, имеющего максимальную зарплату в компании, и название его отдела• Получить список отделов, средняя зарплата в которых больше 1000$
    8. Ado Net – что за технология? и как и когда она используется?
    9. Что такое Entity Framework? Какие подходы проектирования БД знаете? Расскажите про Code First.
    Конечно вопросы про ООП
    1. Назовите и объясните основные парадигмы ООП.
    2. Назовите преимущества объектно-ориентированного подхода к программированию перед структурным программированием
    3. Перечислите недостатки ООП парадигмы.
    4. Что такое раннее и позднее связывание?
    5. Перечислите модификаторы доступа и когда они используются?
    Вопросы про паттерны проектирования
    1. Расскажите про SOLID и примеры его использования
    2. В чем отличие паттерна «Стратегия» от паттерна «Шаблонный метод»?
    3. Паттерн Адаптер и его применение
    4. Расскажите про паттерн «Фасад»
    Ну и конечно огромное количество вопросов по С#
    1. using (SomeClass sc = new SomeClass()){} Что делает данная конструкция?
    2. int i = 1; Console.WriteLine(«i = {0}», ++i); Что выведет данный код?
    3. Различие класса и структуры? И что будет если их передать в метод в виде параметров?
    4. Задача: есть нули и единицы в массиве. Надо для каждого нуля посчитать сколько единиц правее него и вывести сумму таких чисел. Сделать за один проход.
    5. Различие абстрактного класса и интерфейса? Можно ли отказаться от интерфейсов и использовать только абстрактный класс, ведь мы можем в абстрактном классе просто указать сигнатуры методов?
    6. Что такое интернирование строк ?
    7. Расскажите про интерфейс IEnumerable? Зачем он используется?
    8. Когда мы можем пройтись по собственной коллекции foreach- ом? Что для этого надо сделать и почему? (Рассказать про утиную типизацию)
    9. Различие между IEnumerable and IQueryable ?
    10. Как устроен Dictionary внутри? Как борются с коллизиями?
    11. Есть обычный пользовательский класс. Нужно его использовать как ключ в Dictionary. Что для этого надо поменять (добавить) в классе ?
    12. Какова алгоритмическая сложность для операций чтения и записи для коллекции Dictionary?
    13. В чем различие между ключевыми словами «ref» и «out»?
    14. Расскажите как работает try, catch, finally? Когда вызывается каждый
    15. Чем отличаются друг от друга классы String и StringBuilder? Зачем нужно такое разделение?
    16. Какие отличие между значимыми и ссылочными типами? Зачем придумали такое разделение? Нельзя было придумать только либо значимые либо ссылочные?
    17. В чем отличие использования Finalize и Dispose?
    18. Что такое управляемый код и CLR? Основные требования к управляемому коду?
    19. Что такое assembly manifest (манифест сборки)?
    20. Что такое Boxing и Unboxing?
    21. В чем суть полиморфизма?
    22. Чем отличается event от delegate?
    23. Может ли класс реализовать два интерфейса, у которых объявлены одинаковые методы? Если да, то каким образом?
    24. Что такое абстрактный класс? В каком случае вы обязаны объявить класс абстрактным?
    25. В чем разница инкапсуляции и сокрытия?
    26. Что такое частные и общие сборки?
    27. Что такое .Net Framework?
    28. LINQ lazy loading, eager loading в чем разница?
    29. Можно ли запретить наследование от своего собственного класса?
    30. Определение паттерна синглтон
    31. Что такое интеграционные тесты и unit-тесты?
    32. Что такое MVC, MVVM, WEB API?
    33. Каким образом можно присвоить значения полям, которые помечены ключевым словом readonly?
    34. Когда вызывается статический конструктор класса?
    35. Чем отличаются константы и поля, доступные только для чтения?
    36. Чем отличаются константы и поля, доступные только для чтения?
    37. Разница между асинхронностью и параллельностью?
    38. У вас есть сайт, вы заметили что он долго отвечает, как вы будете искать причину?
    Ну и как же без логических задач
    1. Вы находитесь в пустом поезде. Это даже не поезд, а просто вагоны, они сцеплены друг с другом. Все вагоны внутри одинаковы, двери на выход из вагона закрыты, через окна ничего не видно. Вы можете включать и выключать свет в вагоне, в котором находитесь, можете сходить в соседний вагон, там тоже можно включать или выключать свет. Вам известно, что вагоны стоят на кольце и сами сцеплены в кольцо, первый вагон сцеплен с последним, ходить по кругу можно сколько угодно. В момент начала решения задачи в каких-то вагонах свет уже горит, в каких-то — не горит.Ваша задача при помощи управления светом в вагонах и перемещения по ним узнать сколько в этом кольце вагонов.
    2. У вас есть 100 монет по одному рублю. Они все лежат на столе. У вас также есть два носка. Вам нужно распределить все монеты по 2 носкам, так чтобы в одной из них было в два раза больше монет, чем во второй, при этом
      • не разрешается ломать их (они должны остаться целыми)
      • надо использовать все 100 монет (прятать нельзя)
      • они все одинаковые (по весу, по виду)
    Также в одной компании дали готовый пользовательский класс. Мне нужно было найти логические и синтаксические ошибки. К сожалению, это задание мне не дали с собой взять после собеседования и из-за этого не могу опубликовать, но я помню, что там можно было докопаться практически к каждой строчке!В другой компании, попросили спроектировать базу данных для приложения вроде «Инстаграмм». Потом попросили написать сигнатуру метода «Upload», который отправляет фото на сервер. Прозвучал также вопрос что, если бы руководство изменило вот «это», то как бы вы «переспроектировали» эту базу. Ну так как у меня нет опыта в проектировании, мне пришлось на ходу думать и генерировать идеи, как оказалось, я прошел это собеседование).Другая компания решила дать интересную задачу. Я думаю, что это часть задачи, которую решают внутри компании. Решили проверить, как решают другие. Вот как она звучит: Реализовать программу анализа текста. Входной текст произвольный и может быть большим по объему. Количество и содержание метрик определяется самостоятельно. Требования к алгоритму: Программа должна быть расширяема к изменению списку метрик. Масштабируемость. На что обращаем внимание и что анализируем:
    1. Какие метрики предложены кандидатом и их смысл — аналитическое мышление
    2. SOLID анализ предложенного решения — навыки проектирования
    3. Какие шаблоны применены для масштабируемости и расширяемости — архитектурные скилы кандидата. повод при общении затронуть более глубоко вопрос проектирования
    4. Какие синтаксические конструкции языка применены и какая технология используется — использование сахара и средств упрощения кода
    5. Написаны ли unit тесты — что и как тестирует, какие фреймворки и стили тестов использует
    6. Сравнение с когнитивными сервисами — знание трендов применения технологий в реальных проектах
    7. Время, сложность решения, активность дополнительных вопросов выполнения — заинтересованность к решаемым задача/получению оффера
    Так же хочется отметить тот факт, что в своем резюме я указал, что занимаюсь олимпиадным программированием и это очень положительно повлияло на ход многих собеседований. Меня просили рассказать, что это такое. Меня удивило, что большинство интервьюверов не знали про такое движение в программировании. Те, кто знали, просили меня реализовать какие- нибудь сортировки (пузырьковая, вставками, qsort), задачи с олимпиадного программирования. Я считаю, что алгоритмы и структуры данных дают огромный плюс в жизни программиста и теперь как оказалось еще и при трудоустройстве.Также положительным фактом оказалось преподавание информатики в ВЦНМО. Расспрашивали каково преподавать и трудно ли объяснять сложные вещи на пальцах.Для меня было неожиданностью, что меня пригласили работать практически все компании (80%), в которых я проходил собеседование. Может это чувство из-за моей низкой самооценки?! После прохождения собеседований, с уверенностью могу сказать, что это несложно, а даже легко и интересно. Так что друзья, не бойтесь собеседований и крупных компаний, будьте самоуверенными, верьте в свои силы и все будет на 5!Для тех, кто будет готовится или уже готовится к собеседованиям, ниже перечислю ссылки, которые помогут вам (по моему мнению) подготовится к собеседованию на должность Junior C# Developer, .Net Developer.
    1. metanit.com/sharp. Здесь собран большой материал по C#, А также есть специальный раздел «Вопросы к собеседованию». Рекомендую пока пройти теоретический материал, а потом попробовать тесты. Тесты находятся здесь (https://metanit.com/sharp/interview/).
    2. www.quizful.net/test. Сайт направлен именно на собеседования по разным направлениям разработки, где вы также сможете найти и C#. Там есть очень много «острых» и хитрых вопросов.
    3. CLR via C#. Программирование на платформе Microsoft.NET Framework 4.5 на языке C#. Джефри Рихтер. Эта классическая книга по освоению .Net. Там очень хорошо написано про CLR, про сборки и манифесты. Книга написано на довольно простом языке.
    4. metanit.com/sql. Здесь довольно хорошо и просто описывается язык запросов SQL.
    5. www.w3schools.com/sql/default.asp Здесь также можете пройти туториал по SQL. В конце будет тест, который очень легко сдать(опять- таки, это лично субъективное мнение).
    6. tproger.ru/articles/problems Здесь собраны наиболее интересные логические задачи с ответами.
    7. ivinsky.livejournal.com/3266.html здесь собраны задачи по C#, которые часто бывают на собеседованиях
    8. oignatov.blogspot.ru/2015/10/net-developer.html здесь собраны задачи по C#, которые часто бывают на собеседованиях
    9. jopr.org/blog/detail/voprosy-na-sobesedovanii-po-c здесь собраны задачи по C#, которые часто бывают на собеседованиях
    Original source: habrahabr.ru (comments, light).

    https://habrahabr.ru/post/338102/


    Метки:  

    Управление фермой Android-устройств. Лекция в Яндексе

    Воскресенье, 17 Сентября 2017 г. 13:43 + в цитатник
    Leono сегодня в 13:43 Разработка

    Управление фермой Android-устройств. Лекция в Яндексе

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

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




      — Меня зовут Новиков Павел, я работаю в компании «Новые облачные технологии». Мы работаем над продуктом «Мой офис», делаем Android-версию этого офисного комплекта. Само приложение очень большое. Начну с того, что расскажу, как все устроено в плане архитектуры, чтобы после этого перейти к причине, по которой ферма понадобилась. Вы сможете понять, есть у вас события, когда много кода и внешних зависимостей, или же таких событий нет и нужно ли оно вам. Сможете точнее принять решение о том, что ферма — это классно и нужно. Или нет.

      Предыдущий докладчик говорил, что флаттер внутри себя использует SKI. Мы тоже используем, у нас весь натив, все документы полностью рисуются в SKI. Штука очень быстрая, хорошая библиотека.

      В центре приложения «Мой Офис Документы» стоит нативное ядро, CORE, которое делается внутри нашей компании, но делается целой командой. Оно написано на чистом С++, чтобы его потом можно было переиспользовать. Переиспользуется оно всеми командами, которые работают над линейкой наших продуктов.

      Это десктоп, веб, Android, iOS. Даже Tizen есть, его полноценно и весьма успешно делают.

      Самое важное — это что ребята, которые делают CORE, очень тесно завязаны на всех, кто использует этот CORE. Часто происходят ситуации, когда мы просим функционал, который был бы полезен, например, на мобильных клиентах, на iOS, на Android, но на десктопе он не так полезен. Есть такие вещи.

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



      Есть три компонента: нативный CORE, Android-библиотека, в которую этот CORE обернут, и наше Java-приложение. Нативный CORE — чистый С++. Дальше в виде source-кодов он идет в Android-модуль, где сделаны все биндинги для натива, добавлены небольшие прослойки, какая-то логика. Плюс вся отрисовка, которую использует SKI, тоже сделана в этом модуле. Затем модуль как стандартная AR-библиотека вставляется в наше приложение, мы ее используем.

      Всякое происходит — могут появляться баги в нативе, в CORE, в биндингах. Тот же unsatisfying link exception, когда неправильно забиндили Java-класс на нативный класс и получили ситуацию, при которой приложение падает во время обращения к нативному методу. И баги у нас в Java-коде.

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

      Чтобы это делать, нужны тесты — самое лучшее, что может быть в данной ситуации. Наша задача — делать код, работающее приложение. Самый простой способ увидеть, что оно работает, — написать для него тесты. Тесты бывают абсолютно разные. Мы пишем все: unit, UI, интеграционные тесты.

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

      Тесты должны регулярно запускаться на CI. Иначе они бессмысленны. Если у вас есть тесты, которые лежат, то у вас нет тестов.



      С unit-тестами все довольно просто. Запускаем grlu-тест, таску. Она запускается на CI — все хорошо, прогоняются unit-тесты в вашем приложении, вы видите репорты, отчеты.

      Android-тесты запускаются как тесты connect Android, там нужны те же unit-тесты, только они гоняются на девайсах. И тут возникает проблема: UI и интеграционные тесты должны запускаться на реальных устройствах. А CI — не реальное устройство. Можно эту проблему решить несколькими способами.

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

      Можно запускать эмулятор на CI. Это довольно рабочий вариант, тот же Jenkins поддерживает плагин, который позволяет запускать эмулятор, но проблема в том, что эмулятор — это, скорее всего, 86-й эмулятор. А если мы говорим про интеграционные тесты, под интеграцией я в нашем случае подразумеваю внешние зависимости, в частности — именно нативный код, потому что у нас очень много нативного кода. И под интеграционными тестами я понимаю тесты, которые проверяют логику «плюсов».

      Как итог — сделать это возможно, но не очень удобно. Вариант с подключением устройств напрямую не очень удобный, а эмулятор нам не поможет. Можно использовать древний «армовский», но это так себе идея.

      Тут на сцену выходит проект Open STF.

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

      Выглядит это примерно так. Картинка взята из самого проекта. Список устройств доступен, мы можем подключиться к каждому и работать с ним полноценно. То, что вы видите, — живое устройство, которое подключено к ферме и которым можно полностью управлять через веб-интерфейс, полноценно работать.

      Проект Open STF — опенсорсный, и у него есть несколько преимуществ. В первую очередь — работа с реальными устройствами. Как большинство Android-разработчиков, вы понимаете, что ваш код должен проверяться на устройствах. Эмулятор — это хорошо, но есть много вещей, которые нужно проверять на реальных устройствах: тот же натив, работа с SSL. Там много вещей, поведение которых может отличаться. Ферма эту проблему решает.



      Что приятно, для работы с этой фермой вам не нужен root на девайсах. Вы просто подключаете девайс к ферме, и он доступен для работы.

      Это удобный дебаг. Вам ничто не мешает подключиться по DB к устройству, видимому в сети по простому IP, работать с ним как с устройством, которое будто бы подключено к вашему рабочему компьютеру. Экран устройства вы всегда видите, можете с ним взаимодействовать — только не пальцем, а мышкой.

      Устройством могут пользоваться несколько команд. В нашем случае это интересный кейс. У нас обширный парк девайсов в компании и несколько команд, которые с ними работают. Мы — разработчики, тестировщики, все сидим рядом, и девайсы нам нужны для работы. Вторая команда — автоматизаторы, они сидят на другом этаже, но пишут автоматизированные тесты со своим стеком технологий для всех, в том числе для Android. Они тоже имеют доступ ко всем устройствам в компании. Третья — служба поддержки. Им тоже нужно как можно больше устройств. Когда пишут про проблемы в продукте, им нужно их воспроизводить. Проблемы могут быть разные. Преимущество в том, что они имеют доступ ко всем устройствам компании. Это плюс, на устройствах можно запускать приложения и быстрее оказывать поддержку.

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

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

      Есть Rest API, можно подумать про автоматизацию.

      Все девайсы в порядке, в одном месте, всегда заряжены. Бывает, что нужно на одном устройстве что-то воспроизвести, а оно валяется фиг пойми где, разряжено… Приятный бонус.



      Как и у любого проекта, есть недостатки. Не все устройства поддерживаются. Не смогу назвать точных правил. Бывает, подключаете устройство к ферме и оно не определяется. Такое бывает очень редко, у нас был буквально один или два девайса. 95% всего парка работает отлично. Бывает исключение с какими-то китайцами — не определяется и все. Один девайс на 86-м процессоре, фиг знает почему.

      Не очень удобно обновлять. К вопросу об обновлении самого продукта STF: поскольку это open source, обновлением в нашей компании занимается команда девопсов. Это не просто нажать кнопку и обновить. Но нет ничего невозможного. Поскольку речь идет про open source, можно облегчить процесс, проблема не критическая.

      Выпускать за пределы внутренней сети нежелательно. У нас эта ферма крутится внутри сети, и желательно в интернет ей не светить, потому что ферма позволяет получать, по сути, полный доступ над девайсом, там нет ограничений, вы просто работаете с устройством — можете удалить все что угодно, добавить все что угодно. Если что-то можно сделать с реальным устройством у вас в руке — это можете сделать и с фермой. Так что лучше оставлять его для внутреннего пользования.



      Как это выглядит? Внешне есть сервер, где запущена сама ферма, веб-панель, к которой есть доступ.

      Эта веб-панель знает о нодах. Каждая нода — компьютер, к которому подключен девайс. Нод может быть несколько — к вопросу о масштабировании. Сами устройства подключаются не туда, где запущена веб-версия, а к нодам. В нашем случае это выглядит так, что две ноды стоят у нас в команде и еще одна — у саппорта, просто потому что им ближе. Физически не все устройства расположены в одном месте, но все доступны через единый интерфейс, который выглядит вот так.



      Ко всем устройствам написано, что это за продукт, какая версия ОС, SDK level, какие архитектуры у этого устройства. И его location — провайдер, о котором я говорил. Тут два провайдера. Это наши устройства и устройства саппорта. Последние мы стараемся не трогать, это их устройства, доступные через единый интерфейс.

      Сама ферма расположена на GitHub. Первая ссылка — больше рекламная штука.



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

      Проблема была в том, что нужно было как-то подружить эти две вещи. Есть тесты и ферма.



      CI-сервер — это любой сервис, который вам больше нравится. Мы используем Jenkins, у меня примеры с интерфейсом про Jenkins, но вы ни к чему не привязаны.

      У вас есть STF сервер — сам сервер, провайдер, устройства.

      Как их объединять? Очевидно, самый простой способ — Gradle-плагин, который позволяет настроить подключение к ферме при запуске тестов.

      Что он умеет? Довольно базовые вещи. Он выберет устройства, которые вам нужны для запуска тестов, подключится к ним до запуска и отключится по завершении, потому что нехорошо держать девайсы залоченными.

      Что такое нужное устройство? Через плагин вы можете гибко настроить то, какие именно устройства вам нужны. Вы можете отфильтровать их по названию, взять одни Nexus или Samsung, выбрать количество, которое вы хотите отфильтровать. Это может быть один небольшой набор тестов — вы говорите, что хочу на двух девайсах прогнать и убедиться, что ничего не отломалось. Или nightly-прогон сделать, который все девайсы возьмет, проверит, все запустит, все будет отображаться.

      Архитектура. Бывает, нужно запускать тесты на определенной архитектуре. Случаи бывают, но это нужно нечасто.

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

      Еще полезно сортировать по API-уровню. Если вы хотите зачем-нибудь запустить тест на API 21 и выше — это можно.



      Подключается это довольно просто. Как любой Grandle плагин, он интегрируется через подобный синтаксис. Пишете apply plugin, он появится в доступном.



      Сейчас сделан следующий шаг. При запуске нужно привязаться к таске запуска тестов, которая будет запускаться на CI, чтобы плагин работал. Сейчас сделано таким образом. Может, и неудобно, но как есть. Улучшить — не проблема. Главное, что можно привязаться к таске connectToSmartphoneTestFarm. Это основная таска, отвечающая за то, чтобы подключиться к девайсам и отпустить девайсы.



      Ну и третье — настройка параметров фермы. baseUrl — путь, где ферма расположена. apiKey — ключ, чтобы подключаться по REST, это настраивается в консоли фермы. adbPath — чтобы выполнялась операция adbConnect ко всем устройствам, которые будут найдены. Timeout — системная настройка, по дефолту стоит минута. Она нужна, чтобы ферма сама отпускала девайсы, если они по каким-то причинам не используются.



      Так выглядит запуск тестов с использованием фермы. Мы говорим, что connectedDebugAndroidTest запустит все ваши тесты, и сюда передан параметр о том, чтобы не использовать саппорт. Тильда — в данном случае отрицание. Дальше сказать, что я хочу пять устройств, и чтобы они все были –DK21, то есть Lollypop и выше.



      Вот как это выглядит при настройке job внутри Jenkins. Тут эти параметры настраиваются и передаются. Это не часть плагина, job на Jenkins нужно сделать самому. Вы можете не указывать все эти параметры, а сделать одну job, в которой они будут заданы железно. И просто кнопка build, если вам не хочется заморачиваться. Может, мы сделаем так же в дальнейшем.



      Как итог — после прогона всех тестов вы видите самый стандартный HTML-репорт запуска GUnit, только с одним аспектом: вы будете видеть, что они запускались на разных устройствах. Вы будете видеть названия всех тестов, что вы пробежали, и поймете, что они запустились на каждом устройстве. Вы даже увидите, сколько они запускаются по времени, чтобы из этого в дальнейшем строить анализ, чтобы искать какую-то регрессию. Тут полет для фантазии — можно продумать тест, который один и тот же код запустит сто раз и померяет это. И вы увидите, как код на 86-м или на ARM работает: быстрее или медленнее. Ферма в этом поможет, чтобы можно было не руками подключать, а в автоматическом режиме.



      Сам плагин тоже доступен на GitHub, там простенькая документация, но хоть какая. Подключается просто. Весь фидбек приветствуется. Плагин мы написали для себя. Это единственная причина, почему мы не могли нормально ферму использовать. Наконец смогли, порадуемся этому.

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

      Это может быть терминал. Есть ферма, на девайсе произошел какой-то краш, и хорошо бы получить log cut с него, скачать файл, что угодно. Что делать? Либо взять девайс и подключить к себе — но тогда теряется вся магия фермы, — либо использовать какой-то дополнительный клиент.



      Разработали тулзу простую, которая делает все то же самое, но работает через терминал. Вы так же можете подключаться к устройствам, отключаться, выводить их в список, подключаться, чтобы они были доступны в adb, и эта команда говорит: нужны пять Nexus, когда их найдешь — подключись ко всем. После выполнения команды у вас будут в adb доступны пять устройств. Можете что хотите делать из терминала, тоже удобно. Главное преимущество — это быстрее, нежели делать руками. И тоже доступно на GitHub.

      Чисто технически Gradle-плагин и клиент используют нашу библиотеку STL client вместе. Весь сервер написан на Java, есть дальнейшие планы дописать плагин для студии, чтобы девайсы можно было выбирать прямо из UI студии, когда вы работаете. По собственным ощущениям, последние полгода я устройства руками не трогал. Устройства лежат на ферме, я к ним подключаюсь через веб-интерфейс, подключаюсь к adb, копирую путь на ферме и руками девайс не трогаю — лениво. Просто подключился к другому устройству — работаешь с другим.

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



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

      Задача — разделять их. Это то, с чем мы столкнулись, когда начали интегрировать весь процесс работы с фермой и всю описанную автоматизацию. У нас есть как интеграционный тест, так и UI-тесты.

      Есть несколько способов. Самые простые известны. И все рабочие, кому как удобнее.

      Можно написать свой test runner, который будет анализировать, например, названия классов. Рабочий вариант вполне. Договаривайтесь, что вы именуете классы, которые заканчиваются на TestIntegration или TestUI. Вполне рабочий вариант — test runner это разруливает.

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

      Можно использовать вариант с JUnit Suit — классом, который позволяет компоновать тесты. Мы остановились на этом варианте, только потому что он самый простой. У него есть недостатки, но с ним проще всего стартануть — не нужно с Gradle шаманить и писать test runner и переименовывать классы, которые у нас есть.

      Выглядит следующим образом. Просто обновляете класс — в нашем случае это IntegrationSuite — и перечисляете классы, которые должны запускаться. Сразу минус: их нужно явно указывать.



      Когда вы запускаете таску для теста, надо указать отдельным параметром, что я хочу запускать именно данный suite, чтобы запустился только он. Технически так любой тест можно передать, это удобно. Обычно все тесты запустили, они все прошли. В данном случае их можно фильтровать, если вы не используете синтаксис adb instrumentation frame. Все то, что вы видите в студии, когда нажимаете «запустить тесты». Еще это можно шаблонами делать. В данном случае это нужно указать. Тогда будет запускаться только один набор тестов, и он будет выполнять ту изначальную задачу, которую вы перед ним поставили.

      При работе Jenkins запустит набор тестов, и мы сможем узнать, сломалась у нас интеграция или нет. Например, пришла новая версия библиотеки от команды CORE. Команда, которая отвечает за то, чтобы интегрировать ее в наши биндинги, их пишет и выкатывает новую версию их библиотеки, которая в формате AR. И нам нужно как можно более простым способом убедиться, что ничего хотя бы не отломалось. Это уже приятно.

      Раньше то, что отломалось, падало вообще сходу, но если какой-то метод плохо забиндился или значение какого-то параметра поменялось, то мы узнавали только в самом конце цикла, когда проводилось ручное тестирование — например, в restore. И тут приходят тестировщики и говорят, что раньше работало одним образом, а сейчас немного по-другому — почему? А мы не знаем. Начинаем разбираться. Сначала виним одних ребят, потом других.

      В данном случае это делается гораздо быстрее, автоматически. Все артефакты собрались, на Jenkins запустился весь набор тестов. Он подключается к каждому устройству на ферме, и все — мы знаем, что, по крайней мере, все работает как раньше.

      Что можно подытожить? Первое — ферма удобна, когда есть несколько команд. Надо сразу понять, что если вы — один разработчик или работаете над продуктом вдвоем, то вам можно особо не запариваться на эту тему. Если у вас нет внешних зависимостей, если вы делаете простенькое приложение, может, оно вам и не нужно. Разбираться с этим, настраивать… Если вы делаете маленький продукт — скорее всего, у вас нет отдельной команды, ответственной за поддержание инфраструктуры, девопсов. Разруливать самостоятельно тоже можно, но удобнее, когда кто-то делает это за вас.

      Если у вас такого нет, может, оно вам и не надо. Вы живете, все с вами хорошо.

      Другое дело, если у вас появляются внешние зависимости — например, нативный код, причем тот, который постоянно меняется, развивается и дописывается, или у вас идет работа с какими-нибудь секьюрными вещами типа SSL, свои сертификаты, все такое, UI не проверить. Технически можно запустить тесты GUnity на CI или на машине, но в идеале хотелось бы, чтобы такие вещи гонялись на устройствах.

      Тесты полезны. Лютое капитанство: чтобы тесты приносили вам пользу, они должны запускаться и работать с наименьшей болью, чтобы не нужно было руками после какого-то действия брать устройство и подключать руками. Желательно, чтобы оно работало, в идеале само, чтобы можно по job запускать. Либо пусть оно хотя бы работает по нажатию одной кнопки «Проверить». Цель в этом.

      Все. Это доступно на GitHub, сама ферма с описаниями — как поднять, как настроить, что поддерживается. Плагины доступны, клиенты-плагины.

      Этим продуктом пользуемся не только мы — ребята из 2ГИС активно его используют, написали несколько интересных утилит на Python, что тоже позволяет по REST подключаться и выбирать устройство. Мы эту тулзу раньше использовали, но там все не так хорошо.

      Интересная фича, которая легко реализуема. Есть тулза, которая позволяет записывать видео с экрана. Поскольку весь экран гоняется по веб-сокетам, вам ничто не мешает по REST получить веб-сокет, узнать, куда подключиться, и получать все ивенты экрана, работать с ними. Мы для себя это не сделали, в отличие от ребят.
      Original source: habrahabr.ru (comments, light).

      https://habrahabr.ru/post/338038/


      [recovery mode] Немного о доморощенном

      Воскресенье, 17 Сентября 2017 г. 11:54 + в цитатник
      PennyLane сегодня в 11:54 Разное

      Немного о доморощенном

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

      С точки зрения фирм, продающих софт организациям, все, что сделано внутри организаций является доморощенным. Т.е. те дома и кухни, в которых софтфирма сочиняет свои
      продукты, с их точки зрения не являются домами. А вот рабочие места в организациях являются для них домами. Это понятная и удобная для фирм позиция. Эта позиция фирмоцентрична и абсолютно эгоистична: — «Мы не делаем доморощенных продуктов, мы делаем универсальные продукты для всех. Мы не Кулибины.». Т.е., сидя в своих домах и своих кухнях, софтфирмы лучше любых доморощенных разработчиков знают что нужно ВСЕМ. В то время как доморощенные разработчики знают только то, что нужно конкретной организации. Но вот незадача, многие развитые японские организации используют доморощенные разработки, т.к. там наука и инженерия работает на кухнях самой организации без договорных отношений с организациями, знающими обо всем лучше других. Но этот факт мы как бы игнорируем.
      Мы не будем спрашивать, почему японцы соединили науку и инженерию прямо в производственных цехах и офисах. Нас волнует наша прибыль.
      Что нужно софтфирме? Ей нужно продать свой универсальный продукт, подсадить на веки вечные покупателя и доить его, как развитые страны доят колонии. Малейшая доработка должна
      вызывать большой кипеж, накручивание сроков исполнения и цены, в общем, ничего личного. Что при этом происходит в организациях? Малейшая доработка требует бюджетирования, происходят битвы за выделение средств. Со временем люди выбирают путь наименьшего сопротивления, т.е. пользуются тем что есть и молчат в тряпочку, теряют инициативу, т.к. человек действует только тогда, когда у него есть шанс увидеть воплощение своей маленькой рационализации в действии. И действительно, разве большой софтфирме есть дело до маленькой рационализации маленького человечка?
      В результате, до софтфирм не доходит информация о недостатках их систем, у них отсутствует обратная связь и их продукты стареют из-за отсутствия такой информации. Они все больше и больше становятся доморощенными, они все больше и больше вкладываются в рекламу, в промывку мозгов, и совершенствуют свои продукты, в основном, в области рекламы. Кроме того, такая фирма будет все время развивать то, что уже есть, т.к. нельзя выбросить в помойку то, на что были затрачены средства. И выбросит она старье только тогда, когда оно будет очевидно старо и бесполезно. Брэнсон в своей книге писал, как трудно ему было отказаться от производства CD-дисков, хотя уже во всю музыку качали из Интернета. Инерция не в стране, она в мозгах.

      Если разработчики в организациях не решают проблемы в лоб ( а так обычно и бывает ), то презрительные возгласы подростков о доморощенности их разработок представляются мне чем-то псевдопонятийным, малоумным, чего не хотелось бы видеть в Айтишниках, людях настоящего и будущего.
      Original source: habrahabr.ru (comments, light).

      https://habrahabr.ru/post/338090/


      Метки:  

      ASO: ранжирование в App Store и Google Play (найди 10 отличий в алгоритмах)

      Воскресенье, 17 Сентября 2017 г. 09:13 + в цитатник
      Сразу оговорюсь, что точного ответа на большинство рассмотренных ниже вопросов вам не даст никто (если ваш знакомый не работает в Apple/Google и не собрался нарушить NDA), и текущую актуальную версию ответа можно получить только тестами, причем желательно на достаточно большом трафике. И никогда точно не известно, сколько продержится актуальность текущей правильной версии этих ответов.
      Читать дальше ->

      https://habrahabr.ru/post/338086/


      Метки:  

      Смарт контракты Ethereum: пишем простой контракт для ICO

      Воскресенье, 17 Сентября 2017 г. 00:43 + в цитатник
      isvirin сегодня в 00:43 Разработка

      Смарт контракты Ethereum: пишем простой контракт для ICO

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


        Для экономии времени я написал контракт заранее. Давайте разберем его по шагам.

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

        Признаком хорошего тона считается начинать любую программу, в т.ч. и смартконтракт, с указания лицензии, на основе которой она распространяется. В нашем случае это GPL. Также можно указать себя в качестве автора контракта, конечно, если вы не пишете контракт для какого-нибудь скамового проекта, где стесняетесь указать себя в качестве автора.
        /*
        This file is part of the EasyCrowdsale Contract.
        
        The EasyCrowdsale Contract is free software: you can redistribute it and/or
        modify it under the terms of the GNU lesser General Public License as published
        by the Free Software Foundation, either version 3 of the License, or
        (at your option) any later version.
        
        The EasyCrowdsale Contract is distributed in the hope that it will be useful,
        but WITHOUT ANY WARRANTY; without even the implied warranty of
        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
        GNU lesser General Public License for more details.
        
        You should have received a copy of the GNU lesser General Public License
        along with the EasyCrowdsale Contract. If not, see /www.gnu.org/licenses/>.
        
        @author Ilya Svirin <i.svirin@prover.io>
        */
        

        Сразу после идет строка, которая указывает, какую версию компиллятора следует использовать. Если этой строки не будет, то смартконтракт не скомпилируется.

        pragma solidity ^0.4.0;
        


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

        Прежде всего следует понимать, что после загрузки смартконтракта в виртуальную машину Ethereum вы будете взаимодействовать с ним на общих основаниях, как и все остальные пользователи. Логично, что мы, как команда проекта, хотели бы находиться в привилегированных условиях, которые должны как минимум выражаться в том, что контракт должен сформировать именно на командные токены и конечно же отдал именно нам собранный эфир. Для этого контракт должен знать своего владельца и именно за это отвечает контракт «owned».
        contract owned {
        
            address public owner;
        
            function owned() payable {
                owner = msg.sender;
            }
            
            modifier onlyOwner {
                require(owner == msg.sender);
                _;
            }
        
            function changeOwner(address _owner) onlyOwner public {
                owner = _owner;
            }
        }
        


        Контракт «owned» содержит лишь одно публичное поле «owner», значение которого инициализируется в конструкторе значением поля «sender» глобальной структуры «msg», таким образом, изначально владельцем контракта становится тот, кто осуществил его деплой.

        Логично предусмотреть возможность смены владельца на случай, если наш private key будет скомпромитирован, для этого предусмотрена функция «changeOwner», которая получает в качестве параметра адрес нового владельца. Следует обратить на модификатор «onlyOwner», который определен внутри этого же смартконтракта. Модификаторы представляют собой очень удобную конструкцию, позволяющую сгруппировать и присвоить название условиям вызова функций смартконтракта. Модификатор «onlyOwner» проверяет, что вызов функции осуществляется с адреса, который сохранен в поле «owner».

        Контракт «owned» полностью работоспособен и предельно прост, однако несет в себе одну скрытую угрозу, ведь при вызове функции «changeOwner» мы можем по ошибке указать несуществующий адрес, а значит, потеряем контроль над контрактом. Чтобы исправить этот недостаток, достаточно ввести еще поле, назовем его «candidate», а при вызове функции «changeOwner» будем сохранять новое значение сначала в «candidate», а перемещать его в «owner» будем, как только кандидат подтвердит свое вступление в права, вызвав со своего адресу функцию «confirmOwner».

        Следующий в иерархии контракт – «Crowdsale», отвечает непосредственно за сбор средств и выдачу токенов и наследует рассмотренный ранее контракт «owned».
        contract Crowdsale is owned {
            
            uint256 public totalSupply;
            mapping (address => uint256) public balanceOf;
        
            event Transfer(address indexed from, address indexed to, uint256 value);
        
            function Crowdsale() payable owned() {
                totalSupply = 21000000;
                balanceOf[this] = 20000000;
                balanceOf[owner] = totalSupply - balanceOf[this];
                Transfer(this, owner, balanceOf[owner]);
            }
        
            function () payable {
                require(balanceOf[this] > 0);
                uint256 tokensPerOneEther = 5000;
                uint256 tokens = tokensPerOneEther * msg.value / 1000000000000000000;
                if (tokens > balanceOf[this]) {
                    tokens = balanceOf[this];
                    uint valueWei = tokens * 1000000000000000000 / tokensPerOneEther;
                    msg.sender.transfer(msg.value - valueWei);
                }
                require(tokens > 0);
                balanceOf[msg.sender] += tokens;
                balanceOf[this] -= tokens;
                Transfer(this, msg.sender, tokens);
            }
        }
        

        Особое внимание следует обратить на следующие элементы контракта:
        • Публичное поле «totalSupply», которое должно содержать общее количество токенов, выпущенных смартконтрактом;
        • Публичная карта «balanceOf», которое содержит информацию о балансах всех держателей токенов;
        • Событие Transfer, которое должно испускаться смартконтрактом при каждой операции перемещения токенов между держателями токенов.

        Все эти три элемента объединяет одно, они являются обязательной частью стандарта ERC20, который необходимо соблюдать, чтобы информация о наших токенах корректно отображалась в кошельках пользователей и etherscan.io.

        Конструктор смартконтракта «Crowdsale» предельно прост. Прежде всего инициализируется значение поля «totalSupply». Наш контракт выпускает 21 миллион токенов, из которых 20 миллионов сразу будут перемещены на баланс смартконтракта. Будем считать, что токены с адреса смартконтракта как раз и доступны для продажи. Оставшиеся токены, в нашем случае 1 миллион, будут записаны на адрес владельца контракта. Ну и в конце конструктора испускается событие «Transfer», которое помещается в блокчейн и информирует пользователей контракта о том, что с баланса контракта на баланс владельца контракта переведено соответствующее количество токенов. Именно испускание этого события позволит etherscan.io корректно отобразить держателей токенов и их балансы.

        Ну и самая главная функция смартконтракта «Crowdsale», так называемая payback функция, которая вызывается каждый раз, когда эфир поступает на адрес нашего смартконтракта. В самом начале осуществляется проверка, что на балансе смартконтракта есть хоть какое-то количество токенов для продажи. Далее устанавливаем фиксированную цену токенов – 5000 штук за 1 эфир. Затем вычисляем, сколько токенов необходимо отправить отправителю эфира. Количество переданных в транзакции средств записано в поле «value» глобальной структуры «msg» и указано оно в «wei», поэтому при определении количества токенов осуществляем перевод «wei» в «ether».

        Затем осуществляется проверка того, что на балансе смартконтракта есть достаточное количество токенов для продажи. Если запрошено больше токенов, чем есть у смартконтракта, то будем переводить все оставшиеся токены. Определяем стоимость в «wei» оставшихся токенов и возвращаем отправителю излишне переведенный эфир. Убеждаемся, что количество покупаемых токенов ненулевое, после чего записываем это количество токенов на баланс покупателя и списываем их с баланса смартконтракта. В конце не забываем испустить событие Transfer.

        На этом собственно реализация функциональности сбора средств закончена, но теперь нужно сделать наш токен операбельным и реализовать еще некоторые функции стандарта ERC20. Это сделано в контракте «EasyToken», который наследуется от рассмотренного ранее контракта «Crowdsale».
        contract EasyToken is Crowdsale {
            
            string  public standard    = 'Token 0.1';
            string  public name        = 'EasyTokens';
            string  public symbol      = "ETN";
            uint8   public decimals    = 0;
        
            function EasyToken() payable Crowdsale() {}
        
            function transfer(address _to, uint256 _value) public {
                require(balanceOf[msg.sender] >= _value);
                balanceOf[msg.sender] -= _value;
                balanceOf[_to] += _value;
                Transfer(msg.sender, _to, _value);
            }
        }
        

        Прежде всего определим 4 публичных поля, которые сейчас практически не используются никакими кошельками, но для порядка все же определим их. Здесь укажем полное и сокращенное наименование токена, а также количество дробных знаков. В нашем случае токен является неделимым, т.к. значение поля «decimals» установлено равным 0.

        И наконец, единственной функцией смартконтракта «EasyToken», ради которой мы создавали этот контракт, является «transfer», которая также является частью стандарта ERC20 и позволит кошелькам пользователей осуществлять передачу токенов друг другу, переводить их на биржу и выводить их с нее. Реализация функции крайне проста, проверяется достаточность количества токенов на балансе отправителя, после чего баланс отправителя уменьшается, а баланс получателя увеличивается на запрошенное количество токенов. В конце испускается событие «Transfer». Теперь наш токен является операбельным и осталось сделать самое главное – предоставить владельцу контракта возможность вывести собранные эфиры. Это мы сделаем в контракте «EasyCrowdsale».
        contract EasyCrowdsale is EasyToken {
        
            function EasyCrowdsale() payable EasyToken() {}
            
            function withdraw() public onlyOwner {
                owner.transfer(this.balance);
            }
        }
        

        Функция «withdraw» имеет модификатор «onlyOwner», т.е. может быть вызвана только владельцем смартконтракта. Единственное, что она делает – переводит весь баланс смартконтракта на адрес владельца смартконтракта.

        Несмотря на то, что рассмотренный нами смартконтракт является полностью функционально законченным и работоспособным, использовать его в реальном проекте я бы не рекомендовал. Чрезмерное упрощение логики контракта привело к тому, что такой контракт не обеспечивает защиту интересов инвесторов в должной мере, а именно, не устанавливает срок проведения crowdsale, не устанавливает минимальной границы сбора, не возвращает средства инвесторам в случае недостижения минимальной границы, а также содержит ряд известных уязвимостей на уровне компилятора языка Solidity, например, подвержен так называемой short address attack.

        Многие из этих недостатков устранены в смартконтракте нашего проекта PROVER. Контракт PROOF загружен в Ethereum по этому адресу вместе с исходным кодом, с которым можно познакомиться. Можно даже проверить, как работает контракт, отправив на него реальный эфир, у нас как раз сейчас идет Pre-ICO:). На самом деле, мы будем рады, если вы присоединитесь к presale нашего проекта PROVER, который продлится до конца сентября. PROVER – это уникальная технология подтверждения подлинности видеоматериалов на базе блокчейн и видеоаналитики.

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

        Полезные ссылки:
        Original source: habrahabr.ru (comments, light).

        https://habrahabr.ru/post/338084/


        Метки:  

        Мои 5 копеек про Highload Cup 2017 или история 9го места

        Суббота, 16 Сентября 2017 г. 21:00 + в цитатник
        svistunov сегодня в 21:00 Разработка

        Мои 5 копеек про Highload Cup 2017 или история 9го места

          Про Higload Cup уже было несколько статей, поэтому о том, что это было писать не буду, кто пропустил можете почитать в «История 13 места на Highload Cup 2017».

          Так же постараюсь не повторяться и поделюсь интересными, с моей точки зрения, решениями. Под катом:

          1. Немного про структуру данных
          2. Парсинг JSON'а на define'ах
          3. URI unescape
          4. UTF decode
          5. HTTP Server
          6. Тюнинг сети

          и много кода.

          Велосипеды


          Первую версию я написал на Go, используя net/http и encoding/json. И она легла на 2 000 RPS. После этого net/http был заменён на fasthttp, а encoding/json на easyjson. Такая конфигурация позволила уйти спать на первом месте, но с утра я уже был кажется на третьем. Здесь возникла дилемма: оптимизировать код на Go или сразу писать на C++, чтобы иметь более гибкий инструмент ближе к финалу, когда важны будут наносекунды.

          Я выбрал второй вариант, при этом решил использовать только системные библиотеки и написать свой HTTP сервер, который не тратит время на ненужные в данном случае вещи и JSON парсер/сериализатор. Ещё изначально хотелось поиграться с libnuma и SSE 4.2 командами, но до этого не дошло, так как, забегая вперёд, самая долгая операция была write в сокет.

          Весь приведённый ниже код не является «production ready», он написан для конкретных тесткейсов конкурса, в нём нет защиты от переполнения, точнее там вообще нет никакой защиты, использовать его в таком виде не безопасно!

          Немного про структуру данных


          Есть всего 3 таблицы:



          В патронах к танку нашлось чуть больше 1 000 000 пользователей, около 800 000 location'ов и чуть больше 10 000 000 визитов.

          Сервис должен возвращать элементы из этих таблиц по Id. Первое желание было сложить их в map'ы, но к счастью Id оказались практически без пропусков, поэтому можно саллоцировать непрерывные массивы и хранить элементы там.

          Также сервис должен уметь работать с агрегированной информацией, а именно

          • возвращать список посещённых пользователем location'ов в отсортированном по дате посещения порядке
          • возвращать среднюю оценку для location'а

          Чтобы делать это эффективно, нужны индексы.

          Для каждого пользователя я завёл поле std::set, где visitsCmp позволяет хранить id визитов в отсортированном по дате визита порядке. Т.е. при выводе не нужно копировать визиты в отдельный массив и сортировать, а можно сразу выводить в сериализованном виде в буфер. Выглядит он так:

          struct visitsCmp {
              Visit* visits;
              bool operator()(const uint32_t &i, const uint32_t &j) const {
                  if (visits[i].VisitedAt == visits[j].VisitedAt) {
                      return visits[i].Id < visits[j].Id;
                  } else {
                      return visits[i].VisitedAt < visits[j].VisitedAt;
                  }
          }
          

          В случае со средней оценкой location'а, порядок не важен, поэтому для каждого location'а я завёл поле типа std::unordered_set, в котором содержатся в визиты конкретного location'а.

          При любом добавлении/изменении визита нужно было не забывать обновлять данные в затрагиваемых индексах. В коде это выглядит так:

          bool DB::UpdateVisit(Visit& visit, bool add) {
              if (add) {
                  memcpy(&visits[visit.Id], &visit, sizeof(Visit));
          
                  // Добвляем визит в индексы
                  users[visit.User].visits->insert(visit.Id);
                  locations[visit.Location].visits->insert(visit.Id);
          
                  return true;
              }
          
              // Если изменилась дата визита, то надо пересортировать визиты пользователя
              if (visit.Fields & Visit::FIELD_VISITED_AT) {
                  users[visits[visit.Id].User].visits->erase(visit.Id);
          	visits[visit.Id].VisitedAt = visit.VisitedAt;
          	users[visits[visit.Id].User].visits->insert(visit.Id);
              }
          
              if (visit.Fields & Visit::FIELD_MARK) {
                  visits[visit.Id].Mark = visit.Mark;
              }
          
              // Если изменилась пользователь то надо удалить у старого пользователя из индекса и добавить новому
              if (visit.Fields & Visit::FIELD_USER) {
                  users[visits[visit.Id].User].visits->erase(visit.Id);
          	users[visit.User].visits->insert(visit.Id);
          
                  visits[visit.Id].User = visit.User;
              }
          
              // Аналогично, если изменился location
              if (visit.Fields & Visit::FIELD_LOCATION) {
                  locations[visits[visit.Id].Location].visits->erase(visit.Id);
                  locations[visit.Location].visits->insert(visit.Id);
          
                  visits[visit.Id].Location = visit.Location;
              }
          
              return true;
          }
          

          Вообще среднее количество элементов в индексе 10, максимальное — 150. Так что можно было бы обойтись просто массивом, что повысило бы локальность данных и не сильно замедлило модификацию.

          Парсинг JSON'а на define'ах


          Те JSON парсеры, которые я нашёл для C/C++, строят дерево при парсинге, а это лишние аллокации, что в highload неприемлемо. Так же есть те, которые складывают данные напрямую в переменные, но в таком случае нельзя узнать, какие поля были в JSON объекте, а это важно, так как при изменении объекта JSON приходит не с полным набором полей, а только с теми, которые надо изменить.

          JSON, который должен парсить сервис очень простой, это одноуровневый объект, который содержит только известные поля, внутри строк нет кавычек. JSON для пользователя выглядит так:

          {
              "id": 1,
              "email": "robosen@icloud.com",
              "first_name": "Данила",
              "last_name": "Стамленский",
              "gender": "m",
              "birth_date": 345081600
          }
          

          Т.е. довольно просто написать для него парсер на мета языке
          bool User::UmnarshalJSON(const char* data, int len) {
              JSON_SKIP_SPACES()
              JSON_START_OBJECT()
          
              while (true) {
                  JSON_SKIP_SPACES()
          
                  // Конец объекта
                  if (data[0] == '}') {
                      return true;
          
                  // Разделитель полей
                  } else if (data[0] == ',') {
                      data++;
                      continue;
          
                  // Поле "id"
                  } else if (strncmp(data, "\"id\"", 4) == 0) {
                      data += 4;
          
                      JSON_SKIP_SPACES()
                      JSON_FIELDS_SEPARATOR()
          
                      JSON_SKIP_SPACES()
                      // Прочитать и сохранить значение в поле Id
                      JSON_LONG(Id)
          
                      // Выставить флаг, что поле Id было в JSON
                      Fields |= FIELD_ID;
          
                  // Поле "lastname"
                  } else if (strncmp(data, "\"last_name\"", 11) == 0) {
                      data += 11;
          
                      JSON_SKIP_SPACES()
                      JSON_FIELDS_SEPARATOR();
          
                      JSON_SKIP_SPACES()
                      // Прочитать и сохранить значение в поле Id
                      JSON_STRING(LastName)
          
                      // Выставить флаг, что поле LastName было в JSON
                      Fields |= FIELD_LAST_NAME;
          
                  } else if (strncmp(data, "\"first_name\"", 12) == 0) {
                      data += 12;
          
                      JSON_SKIP_SPACES()
                      JSON_FIELDS_SEPARATOR()
          
                      JSON_SKIP_SPACES()
                      JSON_STRING(FirstName)
          
                      Fields |= FIELD_FIRST_NAME;
          
                  } else if (strncmp(data, "\"email\"", 7) == 0) {
                      data += 7;
          
                      JSON_SKIP_SPACES()
                      JSON_FIELDS_SEPARATOR()
          
                      JSON_SKIP_SPACES()
                      JSON_STRING(EMail)
          
                      Fields |= FIELD_EMAIL;
          
                  } else if (strncmp(data, "\"birth_date\"", 12) == 0) {
                      data += 12;
          
                      JSON_SKIP_SPACES()
                      JSON_FIELDS_SEPARATOR()
          
                      JSON_SKIP_SPACES()
                      JSON_LONG(BirthDate)
          
                      Fields |= FIELD_BIRTH_DATE;
          
                  } else if (strncmp(data, "\"gender\"", 8) == 0) {
                      data += 8;
          
                      JSON_SKIP_SPACES()
                      JSON_FIELDS_SEPARATOR()
          
                      JSON_SKIP_SPACES()
                      JSON_CHAR(Gender)
          
                      Fields |= FIELD_GENDER;
          
                  } else {
                      JSON_ERROR(Unknow field)
                  }
          
              }
          
              return true;
          }
          

          Осталось только определить на что заменить команды мета языка и парсер готов:

          #define JSON_ERROR(t) fprintf(stderr, "%s (%s:%d)\n", #t, __FILE__, __LINE__); return false;
          
          #define JSON_SKIP_SPACES() data += strspn(data, " \t\r\n")
          
          #define JSON_START_OBJECT() if (data[0] != '{') { \
                  JSON_ERROR(Need {}) \
              } \
              data++;
          
          #define JSON_FIELDS_SEPARATOR() if (data[0] != ':') { \
                  JSON_ERROR(Need :) \
              } \
              data++;
          
          #define JSON_LONG(field) char *endptr; \
              field = strtol(data, &endptr, 10); \
              if (data == endptr) { \
                  JSON_ERROR(Invalid ## field ## value); \
              } \
              data = endptr;
          
          #define JSON_STRING(field) if (data[0] != '"') {\
                  JSON_ERROR(Need dquote); \
              } \
              auto strend = strchr(data+1, '"'); \
              if (strend == NULL) { \
                  JSON_ERROR(Need dquote); \
              } \
              field = strndup(data+1, strend - data - 1); \
              data = strend + 1; 
          
          #define JSON_CHAR(field) if (data[0] != '"') {\
                  JSON_ERROR(Need dquote); \
              } \
              if (data[2] != '"') {\
                  JSON_ERROR(Need dquote); \
              } \
              field = data[1]; \
              data += 3; 
          

          URI unescape


          В получении списка мест, которые посетил пользователь есть фильтр по стране, который может быть в виде URI encoded строки: /users/1/visits?country=%D0%A0%D0%BE%D1%81%D1%81%D0%B8%D1%8F. Для декодинга на StackOverflow было найдено замечательное решение, в которое я дописал поддержку замены + на пробел:

          int percent_decode(char* out, char* in) {
              static const char tbl[256] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
                      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
                      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
                      -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
                      -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
                      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 10,
                      11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
                      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
                      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
                      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
                      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
                      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
                      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
                      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
                      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
                      -1, -1, -1, -1, -1 };
              char c, v1, v2;
              if (in != NULL) {
                  while ((c = *in++) != '\0') {
                      switch (c) {
                      case '%':
                          if (!(v1 = *in++) || (v1 = tbl[(unsigned char) v1]) < 0
                                  || !(v2 = *in++)
                                  || (v2 = tbl[(unsigned char) v2]) < 0) {
                              return -1;
                          }
                          c = (v1 << 4) | v2;
                          break;
                      case '+':
                          c = ' ';
                          break;
                      }
                      *out++ = c;
                  }
              }
              *out = '\0';
              return 0;
          }
          

          UTF decode


          Строки в JSON объектах могут быть вида "\u0420\u043E\u0441\u0441\u0438\u044F". В общем случае это не страшно, но у нас есть сравнение со страной, поэтому одно поле нужно уметь декодировать. За основу я взял percent_decode, только в случае с Unicode не достаточно превратить \u0420 в 2 байта 0x0420, этому числу надо поставить в соответствие UTF символ. К счастью у нас только символы кириллицы и пробелы, поэтому если посмотреть на таблицу, то можно заметить, что есть всего один разрыв последовательностей между буквами «п» и «р», так что для преобразования можно использовать смещение:

          void utf_decode(char* in) {
              static const char tbl[256] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
                      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
                      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
                      -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
                      -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
                      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 10,
                      11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
                      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
                      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
                      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
                      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
                      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
                      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
                      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
                      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
                      -1, -1, -1, -1, -1 };
          
              char *out = in;
          
              while (in[0] != 0) {
                  if (in[0] == '\' && in[1] == 'u') {
                      uint16_t u = tbl[in[2]] << 12 | tbl[in[3]] << 8 | tbl[in[4]] << 4 | tbl[in[5]];
                      // Все ASCII символы оставляем как есть
                      if (u < 255) {
                          out[0] = u;
                          out++;
                      } else {
                          uint16_t w;
                          // < 'р'
                          if (u >= 0x0410 && u <= 0x043f) {
                              w = u - 0x0410 + 0xd090;
                          // >= 'р'
                          } else {
                              w = u - 0x0440 + 0xd180;
                          }
          
                          out[0] = w >> 8;
                          out[1] = w;
          
                          out += 2;
                      }
                      in += 6;
                  } else {
                      out[0] = in[0];
                      in++;
                      out++;
                  }
              }
          
              out[0] = 0;
          }
          

          HTTP Server


          Парсер


          Из HTTP запроса нужно достать метод (GET/POST), query (path + parameters) и в случае POST запроса тело. Парсить и хранить заголовки нет смысла, за исключением заголовка Content-Lentgth для POST запросов, но как оказалось позже и это не надо, так как все запросы вмещаются в один read. В итоге получился вот такой парсер:

          ...
              auto body = inBuf.Data;
          
              const char *cendptr;
              char *endptr;
              while (true) {
                  switch (state) {
                  case METHOD:
                      body += strspn(body, " \r\n");
                      if (strncmp(body, "GET ", 4) == 0) {
                          method = GET;
                          body += 4;
                      } else if (strncmp(body, "POST ", 5) == 0) {
                          body += 5;
                          method = POST;
                      } else {
                          state = DONE;
                          WriteBadRequest();
                          return;
                      }
                      body += strspn(body, " ");
                      cendptr = strchr(body, ' ');
                      if (cendptr == NULL) {
                          WriteBadRequest();
                          return;
                      }
                      strncpy(path.End, body, cendptr - body);
                      path.AddLen(cendptr - body);
          
                      cendptr = strchr(cendptr + 1, '\n');
                      if (cendptr == NULL) {
                          WriteBadRequest();
                          return;
                      }
          
                      state = HEADER;
                      body = (char*) cendptr + 1;
                      break;
          
                  case HEADER:
                      cendptr = strchr(body, '\n');
                      if (cendptr == NULL) {
                          WriteBadRequest();
                          return;
                      }
          
                      if (cendptr - body < 2) {
                          if (method == GET) {
                              doRequest();
                              return;
                          }
          
                          state = BODY;
                      }
                      body = (char*) cendptr + 1;
          
                  case BODY:
                      requst_body = body;
                      doRequest();
                      return;
              }
          ...
          

          Routing


          Хендлеров совсем мало, поэтому просто switch по методу, а внутри поиск префикса простым сравнением:

          ...
              switch (method) {
              case GET:
                  if (strncmp(path.Data, "/users", 6) == 0) {
                      handlerGetUser();
                  } else if (strncmp(path.Data, "/locations", 10) == 0) {
                      handlerGetLocation();
                  } else if (strncmp(path.Data, "/visits", 7) == 0) {
                      handlerGetVisit();
                  } else {
                      WriteNotFound();
                  }
                  break;
              case POST:
                  if (strncmp(path.Data, "/users", 6) == 0) {
                      handlerPostUser();
                  } else if (strncmp(path.Data, "/locations", 10) == 0) {
                      handlerPostLocation();
                  } else if (strncmp(path.Data, "/visits", 7) == 0) {
                      handlerPostVisit();
                  } else {
                      WriteNotFound();
                  }
                  break;
              default:
                  WriteBadRequest();
              }
          ...
          

          Keep-Alive


          Яндекс.Танк не обращает внимание на заголовок «Connection» в патронах, а смотрит только на этот заголовок в ответе от сервера. Поэтому не нужно рвать соединение, а нужно работать в режиме Keep-Alive всегда.

          Работа с сетью


          Для реализации асинхронного взаимодействия естественно был выбран epoll. Я знаю 3 популярных варианта работы с epoll в многопоточном приложении:

          1. N потоков имеют общий epoll + 1 поток ждёт accept в блокирующем режиме и регистрирует клиентские сокеты в epoll
          2. N потоков имеют N epoll'ов + 1 поток ждёт accept в блокирующем режиме и регистрирует клиентские сокеты в epoll'ах, допустим используя RoundRobin.
          3. Каждый поток имеет свой epoll, в котором зарегистрирован серверный сокет, находящийся в неблокирующем состоянии и клиентские сокеты, которое этот поток захватил.

          Я сравнивал 2 и 3 варианты и на локальных тестах третий вариант немного выиграл, выглядит он так:

          void Worker::Run() {
              int efd = epoll_create1(0);
              if (efd == -1) {
                  FATAL("epoll_create1");
              }
          
              connPool = new ConnectionsPool(db);
          
              // Регистрируем серверный сокет в epoll
              auto srvConn = new Connection(sfd, defaultDb);
              struct epoll_event event;
              event.data.ptr = srvConn;
              event.events = EPOLLIN;
              if (epoll_ctl(efd, EPOLL_CTL_ADD, sfd, &event) == -1) {
                  perror("epoll_ctl");
                  abort();
              }
          
              struct epoll_event *events;
              events = (epoll_event*) calloc(MAXEVENTS, sizeof event);
          
              while (true) {
                  auto n = epoll_wait()(efd, events, MAXEVENTS, -1);
                  for (auto i = 0; i < n; i++) {
                      auto conn = (Connection*) events[i].data.ptr;
          
                      if ((events[i].events & EPOLLERR) || (events[i].events & EPOLLHUP)
                              || (!(events[i].events & EPOLLIN))) {
                          /* An error has occured on this fd, or the socket is not
                           ready for reading (why were we notified then?) */
                          fprintf(stderr, "epoll error\n");
                          close(conn->fd);
                          if (conn != srvConn) {
                              connPool->PutConnection(conn);
                          }
                          continue;
          
                      // Если событие пришло для серверного сокета, то нужно сделать accept
                      } else if (conn == srvConn) {
                          /* We have a notification on the listening socket, which
                           means one or more incoming connections. */
                          struct sockaddr in_addr;
                          socklen_t in_len;
          
                          in_len = sizeof in_addr;
                          int infd = accept4(sfd, &in_addr, &in_len, SOCK_NONBLOCK);
                          if (infd == -1) {
                              if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
                                  continue;
                              } else {
                                  perror("accept");
                                  continue;;
                              }
                          }
          
                          int val = true;
                          if (setsockopt(infd, IPPROTO_TCP, TCP_NODELAY, &val,
                                  sizeof(val)) == -1) {
                              perror("TCP_NODELAY");
                          }
          
                          event.data.ptr = connPool->GetConnection(infd);
                          event.events = EPOLLIN | EPOLLET;
                          if (epoll_ctl(efd, EPOLL_CTL_ADD, infd, &event) == -1) {
                              perror("epoll_ctl");
                              abort();
                          }
                          continue;
          
                      // Событие для клиентского сокета, надо подготовить и отправить ответ
                      } else {
                          bool done = false;
                          bool closeFd = false;
          
                          while (true) {
                              ssize_t count;
          
                              count = read(conn->fd, conn->inBuf.Data, conn->inBuf.Capacity);
                              conn->inBuf.AddLen(count);
                              if (count == -1) {
                                  /* If errno == EAGAIN, that means we have read all
                                   data. So go back to the main loop. */
                                  if (errno != EAGAIN) {
                                      perror("read");
                                      done = true;
                                  } else {
                                      continue;
                                  }
                                  break;
                              } else if (count == 0) {
                                  /* End of file. The remote has closed the connection. */
                                  done = true;
                                  closeFd = true;
                                  break;
                              }
          
                              if (!done) {
                                  done = conn->ProcessEvent();
                                  break;
                              }
                          }
          
                          if (done) {
                              if (closeFd) {
                                  close(conn->fd);
                                  connPool->PutConnection(conn);
                              } else {
                                  conn->Reset(conn->fd);
                              }
                          }
                      }
                  }
              }
          }
          

          Уже после закрытия приёма решений я решил отказаться от epoll и сделать классическую префорк модель, только с 1 500 потоков (Я.Танк открывал 1000+ соединений). По умолчанию каждый поток резервирует 8MB под стек, что даёт 1 500 * 8MB = 11,7GB. А по условиям конкурса приложению выделяется 4GB RAM. Но к счастью размер стека можно уменьшить с помощью функции pthread_attr_setstacksize. Минимальный размер стека — 16KB. Т.к. внутри потоков у меня ничего большого в стек не кладётся я выбрал размер стека 32KB:

              pthread_attr_t attr;
              pthread_attr_init(&attr);
          
              if (pthread_attr_setstacksize(&attr, 32 * 1024) != 0) {
                  perror("pthread_attr_setstacksize");
              }
          
              pthread_create(&thr, &attr, &runInThread, (void*) this);
          

          Теперь потоки занимают 1 500 * 32KB = 47MB.
          На локальных тестах такое решение показало результаты чуть хуже чем epoll.

          Тюнинг сети


          Для профайлинга я использовал gperftools, который показал, что самая долгая операция была std::to_string. Это было довольно быстро исправлено, но теперь основное время было в операциях epoll_wait, write и writev. На первое я не обратил внимания, что, возможно, стоило попадания в призёры, а что делать с write начал изучать, попутно находя ускорения для accept

          TCP_NODELAY


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

              int val = 1;
              if (setsockopt(sfd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val)) == -1) {
                  perror("TCP_NODELAY");
              }
          

          TCP_DEFER_ACCEPT


          Данная опция позволяет отправлять ответ не дожидаясь ACK'а от клиента при TCP handshake'е:

              int val = 1;
              if (setsockopt(sfd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &val, sizeof(val)) == -1) {
                  perror("TCP_DEFER_ACCEPT");
              }
          

          TCP_QUICKACK


          На всякий случай выставил и эту опцию, хотя до конца не понимаю принцип её работы:

              int val = 1;
              if (setsockopt(sfd, IPPROTO_TCP, TCP_QUICKACK, &val, sizeof(val)) == -1) {
                  perror("TCP_QUICKACK");
              }
          

          SO_SNDBUF и SO_RCVBUF


          Размеры буферов тоже влияют на скорость передачи сети. По умолчанию используется около 16KB. Без изменения настроек ядра их можно увеличить до примерно 400KB, хотя попросить можно любой размер:

              int sndsize = 2 * 1024 * 1024;
              if (setsockopt(sfd, SOL_SOCKET, SO_SNDBUF, &sndsize, (int) sizeof(sndsize)) == -1) {
                  perror("SO_SNDBUF");
              }
          
              if (setsockopt(sfd, SOL_SOCKET, SO_RCVBUF, &sndsize, (int) sizeof(sndsize)) == -1) {
                  perror("SO_RCVBUF");
              }
          

          При таком размере появились битые пакеты и таймауты.

          accept4


          Обычно используется функция accept для получения клиентского сокета и 2 вызова fcntl для выставления флага fcntl. Вместо 3 системных вызова нужно использовать accept4, которая позволяет сделать тоже самое передав последним аргументом флаг SOCK_NONBLOCK за 1 системный вызов:

              int infd = accept4(sfd, &in_addr, &in_len, SOCK_NONBLOCK);
          

          aio


          Ещё 1 способ работать с IO асинхронно. В aio есть функция lio_listio, позволяющая объединить в 1 системный вызов несколько write/read, что должно уменьшить задержки на переключение в пространство ядра.

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

          epoll_wait(...., -1) -> epoll_wait(...., 0)


          Как оказалось это была ключевая особенность, которая позволяла уменьшить количество штрафа на примерно 30 секунд, но, к сожалению, об этом я узнал слишком поздно.

          Postscriptum


          Спасибо организаторам за конкурс, хоть всё проходило не очень гладко. Было очень увлекательно и познавательно.
          Original source: habrahabr.ru (comments, light).

          https://habrahabr.ru/post/337854/


          Метки:  

          [Из песочницы] По следам кибер детектива

          Суббота, 16 Сентября 2017 г. 20:30 + в цитатник
          Jarvis7 сегодня в 20:30 Разное

          По следам кибер детектива

          image

          Всем хабровчанам привет!

          Мне довелось поучаствовать в конкурсе, довольно редком в своем роде, под названием Cyber Detective. Конкурс проводился при конференции HackIT-2017, в которой также принимал активное участие. Задания основываются на поиске открытой информации в сети. Хочу поделиться опытом, райтапами и впечатлениями.

          В подобном CTF участвую впервые. Обычно участвую в CTF формата jeopardy, в которых сразу несколько категорий (Web, Reverse, Crypto, Stego, Pwn и тд). В этом же состязании разработчики сделали практически все задания на категорию Recon и одно задание на Forensic. Но об этом ниже. Изначально участвовать в соревновании не планировал, но заинтересовало задание на социальную инженерию, которое в дальнейшем придало уникальности этому состязанию и заставило играть уже серьезно.

          Задания направления Recon, или как их часто называют «задания на OSINT», решаются путем нахождения информации из открытых источников. Для решения таких заданий нужно хорошо уметь пользоваться особенностями поисковых систем, таких как Google, Duckduckgo, Shodan, Censys, знать о различных публичных базах, как правило государственных, прекрасно разбираться в особенностях социальных сетей. Разумеется, никуда без социальной инженерии. И это далеко не все умения, которыми должен владеть квалифицированный интернет-разведчик.

          image

          Всего на Cyber Detective было выложено 27 заданий, которые были разбиты на группы. Эти группы, «ветки» имели свою легенду, историю, вокруг которой крутятся задания и решения. Визуальный граф зависимости заданий, взятый с платформы, представлен выше. История раскрывается по мере решения текущей ветки, что куда интереснее, чем читать скудное условие задания на типичном CTF. К каждой истории в среднем относилось 3-4 задания.

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

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

          Intro


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

          image

          Найти было легко. Зайдя в чат телеграмма и открыв страничку чата, можно было увидеть флаг. Хочу отметить, что флаги не имеют типичного шаблона, вроде flag{...}, или md5. Это затрудняет его поиск. С другой стороны, такой формат можно разместить не везде, так что разработчики решили пожертвовать мои нервы в пользу разнообразности заданий.
          Flag: «Welcome on board!»

          Internet Profile


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

          Start


          image
          В некоторых социальных сетях есть возможность узнать информацию о странице, в частности, это vk.com, зная email/телефон пользователя, а также его фамилию. Баг это, или фича — сложно сказать, но недочет есть (на hackerone бежать репортить не стоит :) ).

          Логично можно подумать, что Марк — это имя, но нет, это фамилия. Это поставило меня в тупик не на долго и не сразу ввел «фамилию». Дальше заходил на ВК, нажимая на «Забыли пароль», ввел номер, фамилию, и получил страницу, которую без проблем можно найти уже через поиск. Вот профиль: Орест Марк

          image
          Flag: «Orest»

          Nick


          image

          Тут все элементарно, его ник, это адрес на сайте ВК.
          Flag: «0n1zz»

          Work


          image

          Просмотрев много групп, на которые он подписан, а также бегло глянув на страницу на гитхабе его, пришла идея, что это Microsoft.
          Flag: «Microsoft»

          Profession


          image

          Решил это задание не сразу, продолжая просматривать задания ниже, и извлекая как можно больше информации со страницы, вводил такие значения как Programmer, Developer, и тд. Затем пошел искать парня в других соц. сетях, нашел его Orest Mark. При поиске, в списке профилей или в информации о странице, написано, что он Software Engineer в Microsoft.
          Flag: «Software Engineer»

          Mail


          image

          Тут было очень просто, вспомнил, что на его странице github видел мыло.
          Flag: «oreest1987@gmail.com»

          Skype


          image

          Само название задания наталкивало на то, что именно искать, ответ на Orest Mark странице.
          Flag: «orest_mark_87»

          Place-1


          image

          Вот с этим и следующим заданиями немного пришлось повозиться, так как формально подходило несколько вариантов. На странице к посту прикреплена геометка, которая не подошла. Просмотрев друзей, сразу понятно, что у Марка есть брат, Тенсон Марк, у которого так же есть геометка. Только посмотрев фейсбук, стало ясно откуда он.
          Flag: «Sinaia, Romania»

          Place-2


          image

          Ответ на фейсбук странице, в графе «О себе».
          Flag: «Kiel, Germany»

          Relatives


          image

          Принимая во внимание, что у Марка есть брат, Тенсон Марк, сдаем очередной флаг.
          Flag: «Tenson Mark»

          Hobby


          image

          Уже достаточно изучив этого человека, понятно, что увлекается он гонками. Правильное название флага можно найти в списках групп на ВК странице, или, если детально приглядеться, то увидеть на аватарке его футболку, где и была эта надпись.
          Flag: «Speed racing»

          Recreation


          image

          Помня, что на ВК странице парня к посту была прикреплена геометка, погуглив место, находим решение.
          Flag: «Sesena, Spain»

          Первая ветка закрыта, задания сданы, +550 баллов буквально за 20 минут. Это дало огромный стимул разбираться дальше, наивно думая, что последующие задания будут такие же легкие. Но посмотрим в итоге, какое досье собрал на парня. Зовут его Орест Марк, знаю его страницы в VK, Facebook, Github. Знаю почту, никнейм, номер телефона. Так же то, что он любит, кем работает, где работает, где живет, где родился, кто его родственники и близкие, где они живут. Эта ветка заданий является прекрасной иллюстрацией того, как можно найти информацию о многих людях через социальные сети. Собранной информации достаточно, чтобы атаковать его почтовый адрес, попробовать получить доступ к аккаунтам в социальных сетях, применить социальную инженерию и узнать любые другие данные. Впрочем, хватит элементарных вещей, перейду к самому интересному заданию этого конкурса.

          Family Dramas


          image

          Сразу же погуглил человека по номеру, пытаться повторить трюк с восстановлением страницы в вк. Гугл выдавал что-то интересное, на первый взгляд, по запросу «0671710968», особенно первая страница. Так и не понял, что это значит. За неимением уже других вариантов решил набрать номер. К моему удивлению, ответила девушка, и сразу сбросил. Задание предполагало применение навыков социальной инженерии, как я понял в последствии, которая помогла бы выведать адрес. Нужно это было сделать не навязчиво, заинтересовав человека. Где-то 20-30 минут думал над легендой, и минут 15 ушло на то, чтобы попрактиковать чтение так, чтобы было не очень заметно, что читаю текст, говоря не навязчиво и не монотонно. Много тонкостей, которые старался учитывать. Вот текст, который составил.
          Добрый вечер!

          Меня зовут Андрей.

          Я представляю движение студентов под названием «World Frendship». Мы занимаемся тем, что объединяем людей, основываясь на взаимопомощи. Помогая в разных жизненных и бытовых вопросах, мы заводим новых знакомых, друзей, а также хотим подарить хоть немного хорошего настроения людям. Хотите поучаствовать?

          — (ответ)

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

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

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

          — (ответ)

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

          — (ответ)

          Тогда нужно уточнить еще пару моментов. Подскажите, как вас зовут?

          — …

          И скажите ваш адрес
          Адрес узнать удалось. Но пришлось потом перезванивать, так как адрес немного не разобрал, и якобы сейчас уточняю по карте адрес для составления маршрута. Уже в процессе разговора понял, что подаю многовато информации, нужно было больше общения. Тем не менее, как позже признались разработчики, это была лучшая попытка из всех, кто получил и не получил адрес. До Кевина Митника мне конечно же далеко, да и кардингом не занимаюсь, но для первого раза сойдет. Хочу отметить, что именно с этого задания начал решать CTF, привлек необычный и очень интересный формат получения ответа. Думаю, админы не спали сутками, так как участников много, и решают они круглые 24 часа. Как потом мне подсказали, можно было еще в телеграмме написать этому номеру, и так же ответили бы, это сделано, думаю, для иностранцев. Хотя конечно же тут совсем не тот драйв, риск, эмоции. По телефону нужно отвечать быстро на вопросы, которые не мог предусмотреть, и качество ответов зависело от грамотности и продуманности легенды. Получив классный опыт, новые впечатления еще больше хотел продолжать решать. Однако такое интересное задание оценивалось в 100 очков, что не порадовало, не справедливо выделили баллы для этого задания на мой взгляд. До игроков в топе скорборда было далеко, но две бессонные ночи это исправили.
          Flag: «Odessa, Palubnaya, 7»

          Retribution


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

          Step #1


          image

          На момент написания статьи гугл не выдавал совершенно никакой информации по этому странному адресу. Это не base64, или какой-либо еще вид зашифрованного текста, как мне сначала показалось. Логично было потом предположить, что это onion ресурс. Скачав Tor браузер через несколько минут лицезрел этот сайт. На момент написания статьи поисковик что-то уже закешировал и это значительно облегчало процесс решения этого задания.

          image

          Вот такой сайт открылся, и получил первый флаг.

          image
          Flag: «HACK IN DARK»

          Step #2


          image

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

          image

          Кликнув на одну из услуг получаю QR код.

          image

          Расшифровка: DRYcucyK5Hfc3A4hit9KqsKm5FwxHJYSdk

          За время CTF эти QR коды менялись, что усложняло решение, немного сбивая с толку. Один из таких кодов успешно расшифровывался через base64 decoder, и получив довольно непонятный текст, пошел по ложному пути, разбирая что это такое. Потом досмотрел, что внизу страницы есть упоминание такой вещи как dogecoin, и сразу стало ясно, что это криптовалюта. Сейчас, когда пишу это описание, в принципе, это кажется очевидным, но на тот момент так не казалось. Погуглив, узнал, что dogecoin — это альткоин, построенный на основе блокчейна. Зайдя на сайт Dogecoin зарегистрировал кошелек, пытался с ним разобраться. Но все оказалось проще, нужно было просмотреть историю транзакций для текущего кошелька. Вот такой список переводов для этого кошелька.

          image

          С криптовалютами сталкивался, но отслеживать операции не приходилось, и это оказалось для меня проблемой. Здесь пересылка монет осуществляется через промежуточные кошельки, которые пересылают эти монеты через другие кошельки и так далее. Всю эту схему можно отследить через программу Maltego, входящую в состав KaliLinux и Parrot OS, а также можно было воспользоваться online сервисами, которые смогли бы это сделать автоматизировано. В процессе решения же делал по довольно простому способу — кликал на транзакции с уходящими монетами из кошелька, выбирая наибольшую сумму. В результате нескольких переходов нашел кошелек, куда стекались монеты. Конечно же сильно усложнили бы работу различные миксеры, но разработчики не стали усложнять и без того тяжелую жизнь участников.
          Flag: «DMqh6vFJ5LpdEbJnW5NYhwRmW5tAC69UmG»

          Step #3


          image

          Это задание оказалось очень простым, за несколько минут сделал. Нужно было погуглить кошелек, его упоминали на нескольких форумах, вот тот форум, который привлек мое внимание благодаря одному положительному отзыву.
          Flag: «w3bg00dua»

          Step #4


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

          image

          Заинтересовал аккаунт на Github, в котором был один репозиторий, и внизу описания был email для фидбеков или предложений.

          image

          С емейлом тоже долго морочался, никуда он не подходил. Спустя время догадался ввести в поиске google+, где и получил ссылку на аккаунт некого GlebReed.
          Flag: «Gleb Reed»

          Какой итог этой ветки? +1000 баллов, много часов потрачено на проработку ошибочных путей решений. Что в итоге разбора этой легенды? Даже если у человека есть сайт в Tor и он недостаточно внимателен к информации о нем в сети, его можно отследить. По кошельку, нику, емейлу, каким-то постам, и так далее, причем делается это исключительно через поиск информации из открытых источников, не говоря о различных закрытых базах или других возможностях, которыми владеют спецслужбы.

          Internet Fraud


          image

          Step #1


          image

          Надолго застрял на этом задании. Но это из-за отсутствия опыта. Решается не сложно, хоть и сам сайт вызвал много вопросов, на которые так и не удалось найти ответ. Заходя на сайт вижу такое приветствие.

          image

          Просканировал сайт на поддиректории утилитой dirsearch, и стало ясно, что сайт работает на Wordpress. Конечно же по привычке начал сканировать wpscan-ом, и прочими утилитами, искать уязвимые плагины, но этого делать не стоило.

          image

          Захожу на страницу авторизации, нажимаю «Lost your password?» и попадаю на страницу сайта.

          image

          Заходя на некоторые страницы меня перенаправляло на дефолтную страницу, которую видел при открытии сайта. Впрочем, на сайте ничего не нашел и начал гуглить. Запрос «site:shop.cyber-detective.hackit.ua» ничего не дал. Но зайдя на ресурс Web Archive, нашел много интересного. Это сервис позволяет хранить снимки сайтов. Даже если сайт будет удален, есть возможность просмотреть html страницы и некоторые картинки. Так же есть возможность делать снимки сайта самостоятельно. Есть снимки за 2 дня.

          image

          В снимках за 3-е число ничего интересного, а вот за 5-е число на странице Contacts есть емейл и скайп.

          image

          Стоит обратить внимание на то, что здесь кривая верстка. Нарочно это сделано, или webarchive сделал это сам, не известно, но зайдя в инспектор элементов, можно увидеть, что емейл отделен тегами. Впрочем, этот емейл ошибочный, нужно продолжать искать. Поиск скайпа ничего не дал, как и поиск по никнеймам. Ответ кроется настранице The best clothes wholesale in UkraineвфайлеWomen’s clothes Spring 2017. Внутри файла ничего интересно, но в свойствах был автор, никнейм которого и являлся частью необходимой почты.

          image
          Flag:«salesmanager@shop.cyber-detective.hackit.ua»

          Step #2

          image
          Сразу понятно, что тут флагом является IP адрес. Скорее всего IP получателя письма. Отправил письмо на этот емейл. Ответа не последовало. Нужно сказать, что отправляя письмо на info@shop.cyber-detective.hackit.ua, приходило письмо с сообщением, что оно не доставлено. Так как ответа на мое последнее письмо не последовало, стал составлять сниффер для перехвата IP адреса того, кто его открывает. Вот такой сниффер у меня вышел.

          
          
          
          
          Добрый вечер!
          Почему не работает сайт shop.cyber-detective.hackit.ua? 
          
          
          

          При использовании конечно же был не mydomain. На моем сервере было два скрипта, один из которых выдавал пиксель и оба записывали IP, UserAgent и прочую информацию. Такой скрипт каждый сможет без труда написать сам. Впрочем, это не сработало, мне ничего на сервер не отстучало. Но спустя пару часов на почту пришло такое письмо (на серве стоял бот, который на все сообщения отвечал одинаково).

          image

          Смотрим подробную информацию о письме.

          image

          image
          Flag:«195.64.154.110»

          Step #3


          image

          Имея небольшой опыт в поиске по фамилиям, судя по полному имени человека, еще и большим регистром записано, предположил, что его найти можно в каком-то государственном реестре.
          Миньюст прекрасно с этим справился. Ввожу ФИО, получаю трех людей. Опытным путем определяю, что человек с местонахождением регистрационного дела «Департамент реєстрації Харківської міської ради» подходит.

          image
          Flag:«80577109515»

          Очередная ветка закрыта и получил +700 очков. Трудно далось только первое задание, но можно сказать, что ветка не сложная.

          OLX Fraud


          image

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

          Step #1


          image

          Тут начал уже составлять досье со всей информацией, которую нашел где-либо. Что знаю? Парня зовут Игорь, номер телефона 380983607320, живет в районе Червонозаводской, в Харькове, а ферма у него вот такая «Майнинг ферма AMD BOX 6 GPU RX 580 180мх/с». Нашел парня в телеграмме, написал ему, звонил, но никто не ответил, и тогда сильно в сторону пошел, как потом понял. Сначала попытался отсеять в ВК тех, кого зовут Игорь, их приблизительно 2 миллиона. Выбрал мужской пол, Украина, Харьков, получилось где-то 22 тысячи. Возможно и получилось бы найти что-то, если бы фамилия была более экзотической. Отсеять из 22 тысяч кого-то не вышло. Гугление картинки ничего не дало, а вот гугление названия фермы дало кое-что интересное. Наткнулся на объявления на других сайтах, вот например это. Я сразу подумал, что правильно решаю, так как фотографии идентичные. Продолжил искать и наткнулся на объявление в ВК, не так давно стало возможно продавать товар там. Нашел страницу парняи потратил огромное количество времени, на то, что бы узнать близких этого человека. Это был ложный след. Вернувшись к этому заданию после сдачи некоторых других, продолжил разбираться с телеграмм аккаунтом, снова писал и звонил, и как в прошлый раз — никто не ответил. Еще спустя время смог понять, как узнать никнейм в телеграмме, зная номер телефона. И это было верное направление. В телеграмме добавил контакт, результат ниже. Можно попробовать что-то написать, но не ответят.

          image image

          Затем нужно зайти в аккаунт, как на первой картинке, и в верхнем правом меню нажать кнопку «Удалить». Пара секунд и получится вот так.

          image

          Смысл в том, что когда удаляем этот контакт, то приложение не выбрасывает в список диалогов, и контакт не исчезает (куда, например, тогда девать переписку), и разработчики телеграмма решили номер\имя заменять на никнейм (и снова не надо бежать на hackerone строчить репорт). Никнейм — Gh0stbust3rs. Беру ранее упомянутый сервис. Здесь снова было огромное количество неверных ходов, так как аккаунтов с таким ником много. Верным решением было выбрать страницу ВК этого человека — Эд Высоцкий. Тут нужно зайти в группы, которые он подписан и сразу бросилась группа с малым количеством подписчиков и странным названием.

          image

          Администратор этого паблика Анна Гостева, которая имеет семейное положение с Эдом Высоцким, значит это наша цель.
          Flag: «04.06.1991»

          Step #2


          image

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

          image

          image

          Нахожу у него фото с геометкой, некий жилой комплекс.

          image
          Flag:«ЖК „5th Avenue“» (в флаге стандартные двойные кавычки)

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

          Facke accounts


          image

          Step #1


          image

          Эта ветка показалась самой сложной. Расспрашивая о верном решении у разработчиков понял, что там все гораздо проще, но делается через способ, известный очень немногим. Впрочем, разберу сначала ложные пути, затем то, как сделал и как нужно было. Есть Наталья Афян. Нужно найти ресторан, запомнил. Детально изучаю страницу этого фейка. Вот фотография, которая выдает владельца оригинальной страницы. Настоящий человек это Ольга Дьякова, которая работает в Slow Food Kiev. Чувствуя, что это не с проста, детально анализирую ее страницу, это никак не помогает. Нахожу несколько статей о ней, вот одна из них, с которой, кстати, были взяты почти все фотографии для фейкового аккаунта. Изучение всего этого материала ничего не дало. Затем стал анализировать лайки, которые стоят под некоторыми постами на стене. Ничего особенного не заметил. Репосты делались на стену из популярных пабликов, тоже искать там не вариант. Оставалось анализировать друзей фейка, которых у нее 133. Под свежую чашку кофе стал открывать каждую страницу. Людей, у которых страница оформлена не на русском, или английском языке пропускал. В результате выделил такую страницу — Mariya Odintsova. Училась в ХНУРЭ, из Харькова и привлекли еще посты на стене, есть два похожих с постами Натальи Афян. Изучая репостыэтого и этого постов, изучая тех, кто делал репост, вижу следующее.

          image

          Наталью Афян и Mariya Odintsova уже знаю, вот кто первые два человека не понятно. Владимир Кулаковский был случайным пользователем, а вот Ольга Пирунова оказалась очень похожей на фейк. Проверил — в друзьях Натальи Афян она имеется. Что же получается? Человек зарегистрировал не один, а три фейковых аккаунта, добавил в каждый аккаунт много друзей и другие свои же созданные фейки. Более того, сделал на всех трех страницах репост 2х одинаковых записей. Это странно, но видя, что это искусственно создавали, понимаю, что продвигаюсь. Вот тут опять застрял, так как ни на одной странице не было каких-либо полезных отсылок к кому-либо еще. Спустя несколько часов изучения друзей понял, что как минимум один аккаунт встречается в друзьях всех трех фейков — Oleg Stanov. На его странице есть пост, на фото которого видно именную салфетницу ресторана.

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

          image

          Теперь о том, как стоило решать. Некоторые вышеупомянутые фейки лайкали вот этот и этот посты Натальи Афян. Среди лайков встретился и Oleg Stanov. Но пока не понятно, что именно он центр всего этого дела, нужно продолжать собирать информацию. Зайдя к тем, кто лайкал Наталью Афян, можно выделить Mariya Odintsova по способу, описанному выше. Теперь воспользуемся фишкой Фейсбука, которой нет в ВК. Нужно зайти на страницу восстановления пароля, ввести имя и фамилию.

          image

          image

          image

          image

          Нажимая на «Раскрыть мои доверенные контакты», нужно ввести выделенное имя и фамилию «Mariya Odintsova». В списке доверенных друзей два фейка, которые находил ранее и страница создателя. Далее, выполняем шаги из способа выше.

          image
          Flag:«Stargorod»

          Step #2


          image

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

          image
          Flag:«Oleg Sotnichuk»

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

          Business and black bookkeeping


          image

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

          Step #1


          image

          Вот такой сайт открылся.

          image

          Прежде чем выйти на правильный след, наткнулся на несколько ложных. Например было несколько сайтов, внешне похожих по интерфейсу, например этот. Так же гугл выдает закешированные страницы, которых на момент прохождения CTF уже не было. Запрос в гугле был site:company.cyber-detective.hackit.ua.

          image

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

          image

          Полученное облако.

          image
          Flag:«cloud.company.cyber-detective.hackit.ua»

          Step #2


          image

          Это задание было самым веселым среди всех, выполнял его в кафе в прикуску с пиццей, и 2 часа хорошо подобранных песен были очень кстати. Решение очевидное, так как это не стандартный CTF и никакой сложной стеганографии здесь не должно быть. Между песнями был вставлен кусок разговора директора с подчиненной, в ходе которого подчиненная сообщила важную информацию. Что бы найти песню, достаточно Проигрывателя Windows Media, понемногу пролистывая музыкальную дорожку вперед. Для ценителей софта скрин ниже. Разговор длился 15 секунд.

          image
          Логин: director
          Пароль: Vladimir-1985

          Aduacity, программа, часто использующаяся в заданиях с музыкальной стеганографией, неплохо иллюстрирует вставленный разговор. Возвращаюсь в облако. После извлечения некоторых файлов из корзины, имею следующее.

          image

          Сразу привлекла картинка с названием Paris.jpg, но флаг не подошел. В папке Photos From Holidays были такие картинки, которые, на мой взгляд слишком круты для фотографий для обычного человека. Возможно это как картинки-воспоминания для человека, который там побывал.

          image

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

          image
          Flag:«San Francisco»

          Step #3


          image

          В прилепленных фотографиях были чексуммы для файлов, рассчитаны через программу 7-Zip.
          Данный таск был из категории Forensic. В облаке был файл «Private Files.tc», а в задании предлагалось скачать «20170906.mem». Первый файл был TrueCrypt контейнером, второй — дампом памяти, в котором спрятаны сессионные ключи от контейнера. Для решения перепробовал такие утилиты как pytruecrypt (для расшифровки с помощью добытых ключей), volatility, Elcomsoft Forensi Disk Decryptor. Подошла программа Passware Kit Forensic, но расшифровать удалось не сразу. Под этот криптоконтейнер подошла Passware Kit Forensic только определенной версии.

          image

          Процесс расшифровки прост, меню «Full Disk Encryption»->«TrueCrypt», далее указываются пути к контейнеру и дампу памяти. Далее процесс расшифровки.

          image

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

          image

          Нужную папку найти просто, достаточно смотреть ее размер и количество входящих в нее файлов. В результате нашел Exel документ.

          image
          Flag:«26,542,579,522.00»

          Мне довелось поучаствовать в уникальном CTF, в котором было огромное количество нестандартных заданий, встречающихся мне впервые. Успешно вышло занять третье место, хотя, имея больше предварительного опыта, смог бы достичь большего. Огромные молодцы победители, вместе боролись за победу многие часы на пролет без сна. Порадовало очень задание на социальную инженерию. Были и не очень приятные задания, но это как обычно на любом CTF. Опыт получил просто колоссальный. Статью старался писать максимально подробно, дабы любой интересующийся по шагам смог повторить мои действия, и более того, понять те ложные пути и ошибки, которые довелось совершить мне.Это поможет гораздо быстрее решать подобные задания в будущем. Задания доступны на сайте еще неделю, дерзайте! Всем же остальным советую быть бдительными, в 21 веке цифровой след — это неотъемлемая часть жизни, и нужно думать, что оставлять в сети.

          Уважаемый читатель, если ты дошел до этого места, поздравляю! Надеюсь ты извлек что то полезное из этой статьи. Если есть вопросы — велкам в ЛС или комментарии.
          Original source: habrahabr.ru (comments, light).

          https://habrahabr.ru/post/338078/


          Метки:  

          [Перевод] Моки и явные контракты

          Суббота, 16 Сентября 2017 г. 19:08 + в цитатник
          Telichkin сегодня в 19:08 Разработка

          Моки и явные контракты

          • Перевод

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


          Ниже представлен вольный перевод статьи, в которой Jos'e Valim — создатель языка Elixir — высказал своё мнение на проблему использования моков, с которым я полностью согласен.




          Несколько дней назад я поделился своими мыслями по поводу моков в Twitter:



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


          Что такое мок?


          Воспользуемся определением из англоязычной википедии: мок — настраиваемый объект, который имитирует поведение реального объекта. Я сделаю акцент на этом позже, но для меня мок — это всегда существительное, а не глагол [для наглядности, глагол mock везде будет переводиться как "замокать" — прим. перев.].


          На примере внешнего API


          Давайте рассмотрим стандартный пример из реальной жизни: внешнее API.


          Представьте, что вы хотите использовать Twitter API в веб-приложении на фреймворке Phoenix или Rails. В приложение приходит запрос, который перенаправляется в контроллер, который, в свою очередь, делает запрос к внешнему API. Вызов внешнего API происходит прямо в контроллере:


          defmodule MyApp.MyController do
              def show(conn, %{"username" => username}) do
                  # ...
                  MyApp.TwitterClient.get_username(username)
                  # ...
              end
          end

          Стандартным подходом при тестирования такого кода будет замокать (опасно! замокать в данном случае является глаголом!) HTTPClient, которым пользуется MyApp.TwitterClien:


          mock(HTTPClient, :get, to_return: %{..., "username" => "josevalim", ...})

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


          Не так быстро. Основная проблема при моке HTTPClient заключается в создании сильной внешней зависимости [англ. coupling везде переведена как "зависимость" — прим. перев.] к конкретному HTTPClient. Например, если вы решите использовать новый более быстрый HTTP-клиент, не изменяя поведение приложения, большая часть ваших интеграционных тестов упадет, потому что все они зависят от конкретного замоканного HTTPClient. Другими словами, изменение реализации без изменения поведения системы все равно приводит к падению тестов. Это плохой знак.


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


          Решение


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


          В Elixir все приложения имеют конфигурационные файлы и механизм для их чтения. Используем этот механизм, чтобы настроить клиент Twitter'a для различных окружений. Код контроллера теперь будет выглядеть следующим образом:


          defmodule MyApp.MyController do
              @twitter_api Application.get_env(:my_app, :twitter_api)
          
              def show(conn, %{"username" => username}) do
                  # ...
                  @twitter_api.get_username(username)
                  # ...
              end
          end

          Соответствующие настройки для различных окружений:


          # config/dev.exs
          config :my_app, :twitter_api, MyApp.Twitter.Sandbox
          
          # config/test.exs
          config :my_app, :twitter_api, MyApp.Twitter.InMemory
          
          # config/prod.exs
          config :my_app, :twitter_api, MyApp.Twitter.HTTPClient

          Сейчас мы можем выбрать лучшую стратегию получения данных из Twitter для каждого из окружений. Sandbox может быть полезен, если Twitter предоставляет какой-нибудь sandbox для разработки. Наша замоканная версия HTTPClient позволяла избежать реальных HTTP-запросов. Реализация этой же функциональности в данном случае:


          defmodule MyApp.Twitter.InMemory do
              def get_username("josevalim") do
                  %MyApp.Twitter.User{
                      username: "josevalim"
                  }
              end
          end

          Код получился простым и чистым, а сильной внешней зависимости от HTTPClient больше нет. MyApp.Twitter.InMemory является моком, то есть существительным, и для его создания вам не нужны никакие библиотеки!


          Необходимость явных контрактов


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


          Мы уже имеем три реализации Twitter API и лучше сделать их контракты явными. В Elixir описать явный контракт можно с помощью behaviour:


          defmodule MyApp.Twitter do
              @doc "..."
              @callback get_username(username :: String.t) :: %MyApp.Twitter.User{}
          
              @doc "..."
              @callback followers_for(username :: String.t) :: [%MyApp.Twitter.User{}]
          end

          Теперь добавьте @behaviour MyApp.Twitter в каждый модуль, который реализует этот контракт, и Elixir поможет вам создать ожидаемый API.


          В Elixir мы полагаемся на такие behaviours постоянно: когда используем Plug, когда работаем с базой данных в Ecto, когда тестируем Phoenix channels и так далее.


          Тестирование границ


          Сначала, когда явные контракты отсутствовали, границы приложения выглядели так:


          [MyApp] -> [HTTPClient] -> [Twitter API]


          Поэтому изменение HTTPClient могло приводить к падению интеграционных тестов. Сейчас приложение зависит от контракта и только одна реализация этого контракта работает с HTTP:


          [MyApp] -> [MyApp.Twitter (contract)]


          [MyApp.Twitter.HTTP (contract impl)] -> [HTTPClient] -> [Twitter API]


          Тесты такого приложения изолированы от HTTPClient и от Twitter API. Но как нам протестировать MyApp.Twitter.HTTP?


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


          Лично я бы протестировал MyApp.Twitter.HTTP на реальном Twitter API, запуская эти тесты по-необходимости во время разработки и каждый раз при сборке проекта. Система тегов в ExUnit — библиотеке для тестирования в Elixir — реализует такое поведение:


          defmodule MyApp.Twitter.HTTPTest do
              use ExUnit.Case, async: true
          
              # Эти тесты будут работать с Twitter API
              @moduletag :twitter_api
          
              # ...
          end

          Исключим тесты с Twitter API:


          ExUnit.configure exclude: [:twitter_api]

          При необходимости включим их в общий тестовый прогон:


          mix test --include twitter_api


          Также можно запустить их отдельно:


          mix test --only twitter_api


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


          1. Изменение в HTTPClient приводит только к падению тестов на MyApp.Twitter.HTTP
          2. Вы не мокаете (осторожно! мок в данном случае является глаголом!) HTTPClient. Вместо этого, вы передаете его как зависимость через файл конфигурации, подобно тому, как мы делали для Twitter API
          3. Вам все еще нужен способ протестировать работу вашего клиента до выкатки в production.

          Вместо создания мока HTTPClient можно поднять dummy-сервер, который будет эмулировать Twitter API. bypass — один из проектов, который может в этом помочь. Все возможные варианты вы должны обсудить со своей командой.


          Примечания


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


          Создание "тестируемого" кода


          Цитата из elixir-talk mailing list:


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

          Я бы сказал, что речь идет не о создании "тестируемого" кода, а об улучшении дизайна [от англ. design of your code — прим. перев.].


          Тест — это пользователь вашего API, как и любой другой код, который вы пишите. Одна из идей TDD заключается в том, что тесты — это код и ничем не отличаются от кода. Если вы говорите: "Я не хочу делать мой код тестируемым", это означает "Я не хочу уменьшать зависимость между компонентами" или "Я не хочу думать о контракте (интерфейсе) этих компонентов".


          Нет ничего плохого в нежелании уменьшать зависимость между компонентами. Например, если речь идет о модуле работы с URI [имеется ввиду модуль URI для Elixir — прим. перев.]. Но если мы говорим о чем-то таком же сложном, как внешнее API, определение явного контракта и наличие возможности заменять реализацию этого контракта сделает ваш код удобным и простым в сопровождении.


          Кроме того, оверхэд минимален, так как конфигурация Elixir-приложения хранится в ETS, а значит вычитывается прямо из памяти.


          Локальные моки


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


          defmodule MyModule do
              def my_function do
                  # ...
                  SomeDependency.heavy_work(arg1, arg2)
                  # ...
              end
          end

          Можно избавиться от зависимости, передав её как аргумент. В данном случае будет достаточно передачи анонимной функции:


          defmodule MyModule do
              def my_function(heavy_work \\ &SomeDependency.heavy_work/2) do
                  # ...
                  heavy_work.(arg1, arg2)
                  # ...
              end
          end

          Тест будет выглядеть следующим образом:


          test "my function performs heavy work" do
              # Симулируем долгое вычисление с помощью отправки сообщения тесту
              heavy_work = fn(_, _) -> 
                  send(self(), :heavy_work)
              end
          
              MyModule.my_function(heavy_work)
          
              assert_received :heavy_work
          end

          Или, как было описано ранее, можно определить контракт и передать модуль целиком:


          defmodule MyModule do
              def my_function(dependency \\ SomeDependency)
                  # ...
                  dependency.heavy_work(arg1, arg2)
                  # ...
              end
          end

          Изменим тест:


          test "my function performs heavy work" do
            # Симулируем долгое вычисление с помощью отправки сообщения тесту
            defmodule TestDependency do
              def heavy_work(_arg1, _arg2) do
                send self(), :heavy_work
              end
            end
          
            MyModule.my_function(TestDependency)
          
            assert_received :heavy_work
          end

          Вы также можете представить зависимость в виде data structure и определить контракт с помощью protocol.


          Передать зависимость как аргумент намного проще, поэтому, если возможно, такой способ должен быть предпочтительнее использования конфигурационного файла и Application.get_env/3.


          Мок — это существительное


          Лучше думать о моках как о существительных. Вместо того, чтобы мокать API (мокать — глагол), нужно создать мок (мок — существительное), который реализует необходимый API.


          Большинство проблем от использования моков возникают, когда они используются как глаголы. Если вы мокаете что-то, вы изменяете уже существующие объекты, и зачастую эти изменения являются глобальными. Например, когда мы мокаем модуль SomeDependency, он изменится глобально:


          mock(SomeDependency, :heavy_work, to_return: true)

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


          Библиотеки для создания моков


          После прочитанного у вас может возникнуть вопрос: "Нужно ли отказываться от библиотек для создания моков?"


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


          Однако, есть библиотеки для создания моков, которые не подталкивают вас на использование описанных выше анти-паттернов. Такие библиотеки предоставляют "мок-объекты" или "мок-модули", которые передаются в тестируемую систему в качестве аргумента и собирают информацию о количестве вызовов мока и о том, с какими аргументами он был вызван.


          Заключение


          Одна из задач тестирования системы — нахождение правильных контрактов и границ между компонентами. Использование моков только в случае наличия явного контракта позволит вам:


          1. Защититься от засилья моков, так как контракты будут создаваться только для необходимых частей системы. Как было упомянуто выше, вряд ли вы захотите прятать взаимодействие со стандартными модулями URI и Enum за контрактом.
          2. Упростить поддержку компонентов. При добавлении новой функциональности к зависимости, вам нужно обновить контракт (добавить новый @callback в Elixir). Бесконечный рост @callback укажет на зависимость, которая берет на себя слишком много ответственности, и вы сможете раньше расправиться с проблемой.
          3. Сделать вашу систему пригодной для тестирования, потому что взаимодействие между сложными компонентами будет изолировано.

          Явные контракты позволяют увидеть сложность зависимостей в вашем приложении. Сложность присутствует в каждом приложении, поэтому всегда старайтесь делать её настолько явной, насколько это возможно.

          Original source: habrahabr.ru (comments, light).

          https://habrahabr.ru/post/338066/


          Метки:  

          [Из песочницы] Классический 2д квест или как прошли наши два года разработки. Часть 1

          Суббота, 16 Сентября 2017 г. 18:58 + в цитатник
          MaikShamrock сегодня в 18:58 Разработка

          Классический 2д квест или как прошли наши два года разработки. Часть 1

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

          Внимание! Под катом есть картинки и видосики, осторожно, траффик!

          image

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

          Поехали!

          Предыстория


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

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

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

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

          Теперь Саня, спустя много лет, вернулся к старой теме и решил запилить ремейк, и даже наваял сюжет, оформленный в виде порядка 60 страниц печатного текста, описывающий все хитросплетения замка. Казалось бы, дизайн-документ даже есть, и дело осталось за малым, подумал Саня и решил сделать компанию на новомодной системе краундфанда, а именно на его российской ветке — boomstarter.

          Видос с тех времен сохранился в недрах ютуба. Давайте посмотрим:





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

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

          Зима — Весна 2014


          Огромным плюсом стало то, что Саня наваял целый 60 страничный документ с полными текстами сцен и даже построил дерево того, как будет развиваться действие в игре. Это, честно говоря, меня немало удивило, потому как дизайн документов я отродясь не видел, и, честно говоря, считал это чутка бредом, еще со времен примеров 1С и их «The Ryaba Revenge», но наличие этого материала сильно облегчило построение игры в дальнейшем.

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

          Итак, план был прост — надо:

          а) делать арт, поскольку его надо много (в игре должно было быть 300+ сцен)
          б) брать и пробовать делать игру на каком-то движке

          С пунктом а) всё было более-менее понятно, но т.к. ни он, ни я рисовать толком не умели, то понятное дело, что учиться на ходу тому, чего никогда не делал — глупо, и Саня начал поиски художника, пока я пробовал копаться с разными движками.

          Движок. Учитывая те особенности игры, которые должны были в ней быть, то требования были простыми — это новелла со своими примочками. Всякие новельные движки я отбросил сразу, из-за возможных проблем со встраиванием дроидовских, ios-овских примочек на этапе запуска, был angengine, но он был только под дроида. Пробовал libgdx, который на тот момент был еще сыроват, cocos2d пугал С++, которым я владел, но после использования С# в университете, к которому возвращаться уже не хотелось. И, наконец, был опробован CoronaSDK, который имел достаточно удобную справку с примерами и плагины, которые обещали быть кроссплатформенными. Движок на тот момент был платным (около 100$), но ради удобства можно было немного потратиться. Язык программирования, используемый Corona, мне тоже в принципе понравился своей простотой — это всем известный скриптовый язык Lua.

          Тем временем, в поисках художника Саня облазил все биржи фриланса и сайты, связанные с гейм-девелоперской тематикой, такие как gcup.ru, gamedev.ru и другие. Темы а-ля мы ищем таланты и делаем супер-игру вызывали тонны срача и комментов диванных экспертов, которые засыпали Саню своими умными заключениями и диагнозами. Впрочем, Сане удавалось успешно слать их всех лесом и спамить-спамить-спамить… Спустя некоторое время, мы всё-таки нашли пару талантов и смогли начать работу.

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

          image

          image

          Спустя пару проб мы-таки пришли к тому что примерно соответствовало желаемому:

          image

          Начало было положено и мы перешли уже к построению сцен самой игры:

          image
          Скетч сцены

          image
          Сцена в монохроме

          image
          Готовая сцена игры

          image
          Первая версия главного экрана игры, позднее она очень сильно изменится

          Дальше случилось то, чего мы не ждали, а именно — человеческий фактор. Мы работали с двумя художниками: вначале они рисовали сцены, взаимодействовали с нами, и всё шло вроде бы хорошо, но в один прекрасный момент художник просто исчезает и больше не выходит с нами на связь. Прождав некоторое время, мы начали поиски другого художника для продолжения работ, ситуация повторилась вновь. Белиссимо!

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

          Тем временем у нас всё-таки добавилось немного арта, из которого я уже собрал демку в CoronaSDK:

          image
          Запуск во встроенном эмуляторе

          image
          Запуск игры на телефоне

          Впечатления о движке? Ну в целом — интересная штука, но теперь, уже смотря на этот движок в ретроспективе, я понимаю, что сделать на таком можно только достаточно простой проектик: ни нормального управления классами, раскиданные данные по куче отдельных .lua файлов, обертки для интерфейса и.т.д. В целом это рабочая система, на которой, если извернуться, то можно сделать проект, но это не сравнится с тем, как если писать на нормальном хорошем ООП-языке типа C# или Java. Таким образом, наваяв еще пару демо, запустив пару раз на телефоне, я всё-таки понял, что целиком писать проект на CoronaSDK я не хочу.

          Помимо этого были испытаны Unity3D (на тот момент в нем нормально не поддерживалась работа с 2д, многое потребовалось бы пилить самому и пытаться завернуть это в 3д пространство, пару дней провозюкавшись с камерой и выполнив пару туториалов я забросил это дело), был также испытан движок Construct для игр на новомодном HTML5, но данный движок был очень сильно заточен под свои компоненты и также откинут в сторону.

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

          Лето-осень 2014


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

          Ну правда не совсем судьбой… У Женьки уже был достаточный опыт работы не только с играми, но и с другими проектами игровой тематики, в том числе и карточными играми, где он также выступал в качестве художника, победы и отметки за участие в конкурсах, ну и.т.д. В общем, человеком он был и есть в плане графики продвинутым!

          image
          Наброски интерфейса

          image
          Один из новых артов

          И один из обучающих роликов созданных самим же автором:





          image
          Скетчи

          В общем, Женька смог! В таком темпе мы и двигались дальше: Саня напару с Женей обсуждали что и как рисовать и ставить в игре, я писал код и пробовал движок LibGDX, потихоньку шло время, и к концу 2014 года у нас было достаточно графики и умения, чтобы выпустить своё первое маленькое приложение в GooglePlay, а именно — демо-версию нашей будущей игры.

          Хотя на тот момент у нас не было готово даже и половины всего того, что надо было, и еще предстояло сделать много, мы всё-таки решили, что надо сделать так по следующим причинам:

          — мы хотели получить опыт публикации приложения в маркете
          — мы хотели посмотреть, как игроки воспримут игру такого жанра (всё-таки текстовые квесты и новеллы — это довольно специфическая ниша)
          — мы хотели посмотреть на доход от рекламы в бесплатном приложении, и как игроки будут реагировать на неё

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

          И в целом — всё было успешно, отзывы на маркете были достаточно позитивными, а средняя оценка на тот момент составила — 4.4 балла, что вроде не так уж и плохо.

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

          Продолжение следует…
          Original source: habrahabr.ru (comments, light).

          https://habrahabr.ru/post/338074/


          Метки:  

          [Из песочницы] ASO в iOS 11: сводная таблица изменений

          Суббота, 16 Сентября 2017 г. 18:23 + в цитатник
          belltane сегодня в 18:23 Маркетинг

          ASO в iOS 11: сводная таблица изменений

          Цель поста — собрать в одном месте актуальную рабочую таблицу, которая поможет участникам сообщества, так или иначе связанным с мобильной поисковой оптимизацией (ASO), оперативно отреагировать на изменения магазина Apple, готовящиеся к релизу 19 сентября.

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



          В этой части вопросов быть не должно. Перейдем к визуальной части:



          Здесь следует обратить особое внимание на механику и порядок размещения скриншотов при наличии и отсутствии видео превью.

          И самое интересное:



          Изменения механики магазина приложений:



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

          Если Вы видите в таблице несоответствие истине, буду рад комментам. Также приветствуются дополнения, на полноту данных этот анализ не претендует.

          Всем неограниченной органики! ;)
          Original source: habrahabr.ru (comments, light).

          https://habrahabr.ru/post/338070/


          Метки:  

          Пишем «Тир» на JavaScript для рекламы на сайте

          Суббота, 16 Сентября 2017 г. 17:56 + в цитатник
          Skaner сегодня в 17:56 Разработка

          Пишем «Тир» на JavaScript для рекламы на сайте

          • Tutorial
          Иногда так бывает, что на сайте хочется разместить рекламу, но сделать это хочется таким образом, дабы она не выглядела навязчиво, и воспринималась пользователем, как что-то интересное и необычное. Один мой приятель подсказал мне идею — сделать на сайте маленький виджет в виде игры, играя в которую пользователь мог бы наблюдать рекламу, либо же видел ее только при проигрыше (или выигрыше).
          Мне захотелось попробовать написать такую игру, и об опыте создания поделиться с другими желающими.
          image


          Так получилось, что играми я занимаюсь уже достаточно давно, и для меня больше сложности составляет грамотно продумать именно вопрос рекламы внутри игры. Поэтому я решил идти по пунктам:
          1. Пользователь заходит на сайт
          2. Видит игру (тут надо его как-то заинтересовать)
          3. Играет (набирает очки)
          4. [если проиграл] Видит рекламу
          5. [если выиграл] Видит рекламу только в другом контексте


          В этой части статьи рассмотрим пункт номер 3 и опишем саму игру, используя язык программирования JavaScript.

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

          Я посмотрел на три кандидата:
          Phaser (навес на Pixi)
          Pixi (отсылка к Phaser из-за рендера, но более низкоуровневый)
          PointJS (от наших)

          И первый движок, с которого я начал, стал PointJS. Зайдя на сайт и потыкав примеры, я уже примерно представил, как будет выглядеть моя будущая игра, прикинул размеры, расположение объектов, хотелось попробовать.
          На официальном сайте так же нашел IDE, которая очень смахивает на Sublime Text, но с некоторыми плюшками типа живого редактирования и отладки прямо в редакторе, для работы даже не нужно ставить браузер.

          Выглядит эта штука вот так:
          image

          При создании проекта базовый каркас приложения уже был выстроен, мне пришлось его менять по следующему принципу:
          Настройки проекта
          image


          Немного о файлах:
          init.js — файл с объявлениями всех глобальных переменных
          game.js — игровой цикла Game
          menu.js — игровой цикл Menu

          init.js — Приготовления


          В данном файле, как упоминал ранее — объявление переменных глобальных.
          // объявляем движок
          var pjs = new PointJS(640, 480, {
          	backgroundColor : 'white' // optional
          });
          
          // растягиваем игру на всю страницу
          pjs.system.initFullPage(); // for Full Page mode
          
          // системные функции для сокращения
          var log    = pjs.system.log;     // log = console.log;
          var game   = pjs.game;           // Game Manager
          var point  = pjs.vector.point;   // Constructor for Point
          var camera = pjs.camera;         // Camera Manager
          var brush  = pjs.brush;          // Brush, used for simple drawing
          var OOP    = pjs.OOP;            // Objects manager
          var math   = pjs.math;           // More Math-methods
          var levels = pjs.levels;         // Levels manager
          
          // включим мышь
          var mouse = pjs.mouseControl.initControl();
          
          // получим новые ширину и высоту экрана
          var width  = game.getWH().w; // width of scene viewport
          var height = game.getWH().h; // height of scene viewport
          
          // заголовок
          pjs.system.setTitle('Tir'); // Set Title for Tab or Window
          
          // отключим показ кусора (заменим своим)
          mouse.setVisible(false);
          
          // переменная для счета
          var score = 0;
          
          // при изменении размера перезагрузим игру
          window.onresize = function () {
            location.reload();
          };
          
          // этой функцией будем прогонять зависимые от высоты экрана значения
          var i = function (num) {
            return num * (height/260);
          };
          


          menu.js — Работаем с меню


          Данный файл отвечает за работу с меню, оно будет показываться перед игрой, во время выигрыша или проигрыша.
          Сам файл (с комментариями)
          // Объявляем игровой цикл
          game.newLoopFromConstructor('Menu', function () {
          
            // Для меню мне требуется создать красивый фон в стиле DNS 
            var bg = game.newImageObject({
              x : 0, y : 0, // позиция
              file : 'dns_logo.jpeg', // файл фона
              w : width, // ширину установим, высота подгонится сама
              alpha : 0.8 // немного блеклый
            });
            
            this.update = function () {
              
              // запомним на всякий случай позицию курсора
              var mp = mouse.getPosition();
          
              // при наведении мыши сделаем ярче
              if (mp.x > 10 && mp.x < width - 10 && mp.y > 10 && mp.y < height - 10) bg.setAlpha(1);
              else bg.setAlpha(0.5);
              
              
              // по клику перейдем в игру
              if (mouse.isPress('LEFT')) {
                game.setLoop('Game');
              }
              
              // отрисуем фон
              bg.draw();
              
              // отрисуем свой курсор
              brush.drawImage({
                x : mp.x - 10, y : mp.y - 10, // 10 - смещение картинки из-за обрамления
                file : 'menu_cursor.png'
              });
              
            };
            
          });
          
          // Запускаем игру на текущем игровом цикле
          game.startLoop('Menu');
          


          И результат:
          image

          game.js — Пишем логику игры


          Тут у нас все достаточно просто: есть три полки, по ним будут «ехать» ноутбуки, на некоторых из них будет логотип DNS, на других синий экран смерти. Задача игрока — убрать с полок все «сломанные» ноутбуки.

          Реализация в коде:
          game.newLoopFromConstructor('Game', function () {
          
            // создадим полки, по которым будут плыть компьютеры
            var bg = game.newImageObject({
              x : 0, y : 0, // позиция
              file : 'bg_game.jpg', // файл фона
              w : width, // ширину установим, высота подгонится сама,
              h : height
            });
            
            // массив с количеством компьютеров на полочках (для инициализации)
            var cntShelf;
            
            var cntRIP = 0;
            
            // уровни полок по оси Y
            var shelf = {
              1 : i(15),  // верхняя полка
              2 : i(86),  // средняя
              3 : i(160)  // нижняя
            };
          
            // скорости движения на полках  
            var shelfSpeed = {
              1 : math.random(2, 8),
              2 : math.random(2, 8),
              3 : math.random(2, 8)
            };
            
            var createLaptop = function (s) { // агрументом передаем полку
              var rnd = s ? s : math.random(1, 3); // выбираем полку (случайно) или из агрумента
              var rip = math.random(1, 5) == 5; // выберем тип компьютера (случайно)
              
              // увеличим количество компьютеров, которые надо убрать (для инициализации)
              if (!s && rip) cntRIP++;
              
              return game.newImageObject({
                y : shelf[rnd], // установим на выбранную полку
                h : i(50), // выота компьютера
                file : !rip ? 'laptop.png' : 'laptop_rip.png', // выбираем нужный спрайт
            
                userData : { // дополнительные данные (нестандартные)
                  shelf : rnd, // полка
                  rip : rip // тип компьютера
                },
            
                onload : function () { // отпозиционируем после загрузки по оси X 
                  var dist = 10; // дистанция между компьютерами
                  this.x = -cntShelf[rnd] * (this.w + dist); // рссчитываем позицию
                  cntShelf[rnd]++; // увеличиваем количество на выбранной полке
                }
              });    
            };
            
            // объекты для "отстрела"
            var cells = []; // это массив
            
            var fog = 0; // степень прозрачности после тумана после выстрела
            var scale = 1; // степень деформации прицела
          
            // при входе в игровой цикл установим первичные данные
            this.entry = function () {
              score = 0; // сбросим счет
              
              // сбросим количества компьютеров
              cntShelf = {
                1 : 0,
                2 : 0,
                3 : 0
              };
              
              // заполнение массива
              OOP.fillArr(cells, math.random(20, 90), function () {
                return createLaptop(); // вернем в массив только что созданный объект
              });
            };
          
          	this.update = function () {
          	  var dt = game.getDT(30); // для плавного движения возьмем дельту и поделим на 100
          	  
              // запомним на всякий случай позицию курсора
              var mp = mouse.getPosition();
              
              // запомним так же скорость мыши
              var mps = Math.max(mouse.getSpeed().x, mouse.getSpeed().y);
              
              // запомним состояние нажатия (true/false) для текущего кадра
              var fire = mouse.isPress('LEFT');
          
              // имитируем выстрел
              if (fire) {
                fog = 1;
                scale = 1.5;
              }
          
              // рисуем полочки
              bg.draw();
              
              // рисуем объекты
              OOP.forArr(cells, function (c, i) {
                var rnd;
                c.draw();
                c.move(point(shelfSpeed[c.shelf] * dt, 0)); // двигаем по оси X
                
                // если объект ушел за пределы видимости, меняем его позицию по X
                if (c.x > width) {
                  c.x = -c.w * cntShelf[c.shelf];
                }
                
                // если выстрел, проверим попадание
                if (fire) {
                  // если курсор попадает в статический бокс объекта BBOX
                  if (mouse.isInStatic(c.getStaticBox())) {
                    // если компьютер с синим экраном - то увеличиваем счет
                    if (c.rip) {
                      score++;
                      
                      // если мы набрали нужное количество очков
                      if (score >= cntRIP) {
                        score = 1;
                        game.setLoop('Menu');
                      }
                      
                    } else { // а иначе выкидываем в меню из-за неправильного выбора
                      score = -1;
                      game.setLoop('Menu');
                      return;
                    }
                    
                    cells.splice(i, 1);
                    cells.push(createLaptop()); // добавляем еще новый компьютер
                    return;
                  }
                }
                
              });
              
              
              // рисуем прицел
              brush.drawImage({
                x : mp.x - i(50) * scale, y : mp.y - i(50) * scale, // 10 - смещение картинки из-за обрамления
                w : i(100) * scale, h : i(100) * scale, // размеры установим фактические
                file : 'cell.png'
              });
              
              // рисуем счет
              brush.drawText({
                text : 'Убрано: ' + score + '/' + cntRIP,
                color : 'black',
                size : i(15),
                font : 'serif'
              });
              
              // если есть туман, рисуем его и меняем прозрачность
              if (fog > 0) {
                brush.drawRect({
                  w : width, h : height, // закрываем всё
                  fillColor : 'rgba(255, 255, 255, '+fog+')'
                });
                
                fog -= 0.1; // прозрачность
                scale -= 0.05; // степень деформации прицела
              }
              
              
          	};
          
          });
          


          Результат:
          image

          Рекламные вставки и переработка menu.js


          Теперь нам надо переработать файл menu.js, и ввести в него дополнительные условия, что выводить, когда score == -1 (проиграл) и score == 1 (выиграл).

          Переделанный файл menu.js выглядит:
          // Объявляем игровой цикл
          game.newLoopFromConstructor('Menu', function () {
          
            // Для меню мне требуется создать красивый фон в стиле DNS 
            var bg = game.newImageObject({
              x : 0, y : 0, // позиция
              file : 'dns_logo.jpeg', // файл фона
              w : width, // ширину установим
              h : height,
              alpha : 0.8 // немного блеклый
            });
            
            // объявим функция входа в игровой цикл
            this.entry = function () {
              
              var div, _s;
              
              if (score) {
                div = pjs.system.newDOM('div', true);
                _s = div.style; // ссылка на стили
                _s.height = _s.width = '100%'; // установили ширину и высоту HTML блока
                _s.backgroundColor = 'white'; // заливка
                _s.cursor = 'default'; // курсор
          
                if (score == 1) { // если выиграл
                  div.innerHTML = `
                    
          Вот так мы оставляем на прилавках только качественный продукт!

          Перейти в магазин и попробовать лично
          Сыграть еще раз
          `; } else if (score == -1) { // если проиграл div.innerHTML = `
          Задача - убрать только сломанные компьютеры!

          Я понял, запускай!
          Перейти в магазин и попробовать лично
          `; } } }; this.update = function () { // запомним на всякий случай позицию курсора var mp = mouse.getPosition(); // при наведении мыши сделаем ярче if (mp.x > 10 && mp.x < width - 10 && mp.y > 10 && mp.y < height - 10) bg.setAlpha(1); else bg.setAlpha(0.5); // по клику перейдем в игру if (mouse.isPress('LEFT')) { game.setLoop('Game'); } // отрисуем фон bg.draw(); // отрисуем свой курсор brush.drawImage({ x : mp.x - 10, y : mp.y - 10, // 10 - смещение картинки из-за обрамления file : 'menu_cursor.png' }); }; }); // Запускаем игру на текущем игровом цикле game.startLoop('Menu');


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

          Посмотреть живой пример: Запустить в браузере
          Original source: habrahabr.ru (comments, light).

          https://habrahabr.ru/post/338058/


          Метки:  

          Приглашаем на Android Devs Meetup 22 сентября

          Суббота, 16 Сентября 2017 г. 16:36 + в цитатник

          Метки:  

          Стриминговый сервис Roku собрался на IPO: что может пойти не так

          Суббота, 16 Сентября 2017 г. 13:26 + в цитатник
          itinvest сегодня в 13:26 Разное

          Стриминговый сервис Roku собрался на IPO: что может пойти не так



            Изображение: Mike Mozart, CC BY 2.0

            Сервис потокового телевидения Roku Inc. подал документы для первичного размещения акций на бирже. Крупнейший игрок американского рынка ожидает оценки в $1 млрд. Однако, компания заявила и о ряде рисков, которые, по мнению инвесторов, могут помешать успешному IPO.

            Что такое Roku


            Roku наиболее известна своими приставками, которые позволяют транслировать Netflix и другое потоковое видео. Компания возникла как подразделение Netflix Inc., который инвестировал в неё $5,7 млн. В 2009 году Netflix продал свою долю за $1,7 млн. Но Roku до сих пор работает с этой мультимедийной компанией и даже арендует офисное помещение у Netflix.

            Свою первую ТВ-приставку Roku выпустила в 2007 году. На тот момент стриминговых устройств с аналогичным функционалом практически не было (за исключением Apple TV), и Roku быстро завоевала рынок. Сегодня она – лидер в США в гаджетах интернет-телевидения.

            Roku по существу продает три вещи: свое потоковое оборудование, которое работает с телевизорами, рекламу и партнерский контент. Последние два пункта в этом списке Roku обозначает как «доход от платформы» – сегмент, который растет, в то время как доходы от аппаратных средств, по-видимому, снижаются.

            Не обошлось у Roku и без скандалов. Компания столкнулась с проблемами при продаже своих устройств в Мексике – хакеры начали транслировать пиратский контент от HBO, ESPN и других через приставки Roku. В результате мексиканский суд заблокировал продажи устройства по всей стране стране, что стоило компании почти $2 млн.

            Выход на IPO


            Слухи об IPO Roku ходят уже давно – компания задумывалась о нём ещё в 2014 году, но предпочла оставаться частной. Однако в 2017 году Roku всё же пересмотрела своё решение и наняла андеррайтеров. Ими стали Morgan Stanley, Citigroup и Allen & Co. Одной из причин публичного размещения акций Roku в этом году может стать желание успокоить людей, которые помогли финансировать компанию на раннем этапе.

            Возможно также, что менеджмент компании был вдохновлен примерами двух других технологичных IPO – Snap Inc. и Blue Apron Holdings Inc. Размещение Snap Inc. стало рекордным по объемам, акции компании в день IPO торговались по $17 за штуку, но уже 1 сентября упали в цене до $14,27. Тем временем стоимость Blue Apron снизилась на 52% с момента его IPO.

            Что произойдёт с Roku, пока непонятно. За первые шесть месяцев 2017 года продажи приставок компании снизились на 2% до $117,3 млн по сравнению с аналогичным периодом прошлого года. Хотя компания фактически продает на 37% больше оборудования, в заявлении об IPO говорится, что она снижает средние отпускные цены. С другой стороны, доходы от платформы выросли на 91% до $43,1 млн за тот же шестимесячный период.

            Что касается деталей IPO, Roku решила предложить два класса акций для инвесторов. Обыкновенные акции класса A, принадлежащие руководителям, директорам и нескольким ранним инвесторам, будут равняться одному голосу за акцию. Акции класса В будут равняться 10 голосам за акцию и конвертируется в одну акцию обыкновенных акций класса А.

            Этот шаг позволяет инсайдерам сохранять более сильный контроль над компанией после IPO. Сейчас 28,4% акций компании принадлежат CEO компании Энтони Вуду, 35% – венчурной компании Menlo Ventures, 12,9% – фонду Fidelity Investments Inc. и 7% – 21st Century Fox Inc.

            Что может пойти не так


            Roku является лидером в области потокового телевидения, на долю которого приходится 27% американского рынка. И тем не менее, эксперты не видят чересчур радужных перспектив для IPO компании.

            Основный бизнес Roku сейчас – это продажа устройств. Недавние размещения компаний, которые специализируются на продаже продуктов, оказались не слишком успешными. Например, цена акций GoPro упала на 38% в прошлом году и торгуется на 63% ниже цены IPO в 2014 году. Другой игрок – Fitbit – добился еще худших результатов: акции снизились на 61% за последние 12 месяцев и на 71% в сравнении с первоначальной ценой в 2015 году.

            Та же судьба, вероятно, ожидает и Roku – продукт насыщает рынок, после чего рост доходов компании постепенно угасает. Особенно если учесть, что у Roku есть сильные конкуренты (приставки от Apple и Google) с более крупными бюджетами и более узнаваемыми брендами. Сейчас компании приходится буквально бороться за полки в розничных магазинах. Усугубляет ситуацию и то, что один из крупнейших онлайн-продавцов Roku – Amazon.com – также запустил конкурирующий продукт и, вероятно, вовсе откажется предлагать на своем сайте устройства Roku.

            Однако, в отличие от Fitbit и GoPro у Roku есть шанс заверить инвесторов в своей перспективности. В июле 46% своей выручки компания получила от подписки и рекламы на платформе. Что еще более важно, продажи в этом сегменте выросли на 95%, превратив Roku в производителя ПО и контентную компанию.

            Когда зрители смотрят CBS News и Vice, Roku получает доходы от рекламы. Также компания получает доход от подписных сервисов, таких как HBO Now и Hulu. Некоторые производители телевизоров платят за внедрение сервиса Roku в свои устройства. На предстоящем roadshow Roku, вероятно, попытается убедить инвесторов в том, что будущее компании как раз в этих направлениях бизнеса.

            Другие материалы по теме финансов и фондового рынка от ITinvest:


            Original source: habrahabr.ru (comments, light).

            https://habrahabr.ru/post/338056/


            Метки:  

            [Перевод] Твой софт никому не нужен. Или почему разработка ПО требует свежего подхода

            Суббота, 16 Сентября 2017 г. 11:19 + в цитатник

            Метки:  

            Обновления Magento, Защита от вредных администраторов, утечки данных, исполнения кода

            Пятница, 15 Сентября 2017 г. 22:07 + в цитатник
            kirmorozov вчера в 22:07 Разработка

            Обновления Magento, Защита от вредных администраторов, утечки данных, исполнения кода

              image

              Обновления Magento 2.1.9, 2.0.16, принесли множество заплаток в том числе от XSS, CSRF, неавторизированные утечки данных, защита от администраторов/операторов магазинов.
              Зацепило даже Magento 1.x, 1.9.3.6 и 1.14.3.6 получили обновления.

              Для простоты назовем плохого администратора/оператора — Одмин.
              Обновление: Добавлено описание того как происходит утечка данных о заказанных товарах.

              Критичный (1)


              APPSEC-1800: Remote Code Execution vulnerability in CMS and layouts
              Дословно: Одмин может написать код, который будет исполнен на сервере.
              Это все благодаря layoutам, в которых можно было вызывать исполнение кода.
              Когда-то можно было написать в Magento 1.x такую конфигурацию, которая позволяла показать содержимое любого файла на странице товара.
              Скорее всего используется что-то очень похожее или какой-то из блоков позволяет задать ему «вредный параметр».

              Высокий риск (3)


              APPSEC-1887 Одмин мог прочитать то, что не стоит читать.
              APPSEC-1850 Одмин мог удалить что-то очень важное, остановить работу магазина
              APPSEC-1851 RCE Одмин мог исполнить вредоносный код, последствия мы можем себе представить.

              Средний риск (29)


              APPSEC-1567 Покупатель мог получить данные о заказах, после доступа к какому-то, но нужно постараться сделать cookie.
              APPSEC-1769 Одмин мог записать sitemap не туда
              APPSEC-1713 Можно получить важные данные по системным ссылкам
              APPSEC-1852 XSS и CSRF Одмин мог заложить XSS и CSRF
              APPSEC-1482 Возможность отправить «не туда»
              APPSEC-1502 XSS Одмин мог назвать тип товара
              APPSEC-1494 XSS Если кто-то между сервером и xml с новостями вставит свой xml.
              APPSEC-1793 CRE Для серверов с Nginx можно закинуть и выполнить файл
              APPSEC-1819 Перехват сессии клиента, которая не устарела
              APPSEC-1802 CSRF Можно было захватить управление учетными записями клиентов
              APPSEC-1493 XSS В названии страницы можно было вписать код
              APPSEC-1755 CSRF Можно было наделать дел после входа клиента в систему
              APPSEC-1853 XSS и CSRF Одмин мог в модуле рассылок добавить
              APPSEC-1729 XSS Одмин мог добавить код в названии статуса заказа
              APPSEC-1591 XSS Одмин мог заложить в картинках товаров проблему
              APPSEC-1896 XSS Одмин мог в заказе заложить вредный код
              APPSEC-1673 XSS Одмин через SVG favicon подкинуть проблему
              APPSEC-1773 DoS Одмин мог указать ID страницы при создании лишить возможности создавать новые
              APPSEC-1577 XSS Одмин мог добавить код при активации интеграций
              APPSEC-1510 Одмин с ограниченными правами мог подменить favicon
              APPSEC-1545 XSS Одмин мог списать код в поля клиента
              APPSEC-1535 Одмин мог редактировать значения через быстрое редактирование в таблицах.
              APPSEC-1588 Вредитель мог «слить» данные о всех предыдущих заказах во время оформления заказа
              APPSEC-1701 Возможное переиспользование сессий API
              APPSEC-1630 Можно было ознакомиться со статусом обновления системы
              APPSEC-1628 Получение абсолютного пути на сервере
              APPSEC-1599 Браузер пытается использовать авто-подстановку входе в админ-панель

              Низкий риск (2)


              APPSEC-1709 Можно было получить почту администратора
              APPSEC-1495 Редактирование поле заказа без права просмотра

              Интересные атаки


              Интересны тем, что их можно отработать по Magento 1.x.
              APPSEC-1793 nginх очень часто применяется в боевых системах.
              APPSEC-1588 Можно было достать данные о клиентахкупленных товарах у конкурента.
              Используя идентификаторы строк заказа, пытаемся добавить товар к себе в корзину по идентификатору строки из заказа ранее, товар будет добавлен.
              Это не дает времени, заказа и даты, но если начать «следить» за магазином с момента X, то можно собрать данные о том, что покупали клиенты начиная с момента X. Чтоб собрать такие данные, нужно регулярно опрашивать систему.
              В момент X нужно узнать идентификатор последнего заказанного товара.
              Также можно собрать базу самых продаваемых товаров магазина, если товары не удалялись.

              Вместо концовки
              18 из 35 уязвимостей — атака изнутри, когда администратор магазина вредит самостоятельно.

              Агентствам и поставщикам модулей
              Стоило бы обновить свои демо-стенды, чтоб не пришел Одмин.
              Владельцам магазинов
              1. Самая пора проверить списки администраторов, чтоб среди них не оказалось Одминов.
              2. Стоило бы обновиться, чтоб не отдавать данные о заказах конкурентам или спамерам.

              Источник: https://magento.com/security/patches/magento-2016-and-219-security-update
              Патчи можно посмотреть на Гитхабе: 2.015-2.0.16 и 2.1.8-2.1.9

              Обновление: Добавлено описание того как происходит утечка данных о заказанных товарах.
              Original source: habrahabr.ru (comments, light).

              https://habrahabr.ru/post/338052/


              Метки:  

              Анализ статьи «Начальник, хочу работать из дома»

              Пятница, 15 Сентября 2017 г. 19:16 + в цитатник
              ARadzishevskiy сегодня в 19:16 Управление

              Анализ статьи «Начальник, хочу работать из дома»

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

                Статья привлекла меня 3-мя интересными на мой взгляд аспектами: 1) сама тема очень злободневна и статья получила множество комментариев, 2) публикация набрала рейтинг более 100, 3) при этом она не несет в общем-то никаких реальных объяснений, а построена на очень спорных аргументах — лозунгах.

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

                Статья написана, по словам ее создателя, чтобы «… доказать, что удаленная работа не вредит бизнесу, а только помогает ему». Но то, что я прочитал, меня не убедило. Автор, не обижайтесь, я просто хочу разобраться в этой теме вместе с Вами более подробно. Я 18 лет проработал программистом, 8 системным аналитиком и PM, последние 3 года еще и руковожу ИТ фирмой. Кой-то опыт накопил, участвуя в разных ролях ИТ деятельности. Давайте вместе пройдемся сначала по аргументам, а дальше попробуем провести анализ практики удаленной работы из дома. Нет, я не хочу сказать, что организовать работу из дома, да еще эффективно, нельзя. Конечно можно. Но давайте не полагаться только на впечатления, а проанализируем ситуацию и попробуем систематизировать информацию о подобной организации работы.

                Цель своей публикации я бы определи так: Выявить причины, влияющие на эффективность работы ИТ специалистов, и установить способы, повышающие ее. Рассмотрение мы будем проводить в рамках вопроса организации рабочего места ИТ специалиста.
                Итак, начнем с аргументов «за» удаленную работу из дома, приведенные в вышеуказанной статье:

                1. «Возможность не тратить на дорогу до работы драгоценное время. … стресс, который приносит общественный транспорт… Все это сказывается на продуктивности и здоровье сотрудников». Тут не поспоришь железобетонный аргумент, полностью согласен.
                2. «Возможность работать в комфортных условиях. Согласитесь, мало кто на вопрос «где вам хорошо работается» ответит «в офисе». Или уточнит про раннее утро, когда никого нет. Не все понимают, что работа программиста – это творческая работа, которая требует сосредоточенности и концентрации внимания». А вот с этим абсолютно не согласен. Нет, конечно если у Вас большой особняк с собственным кабинетом, и дети с прочими домочадцами приучены не забегать в него без стука, а тихо проходить мимо на цыпочках в мягких тапочках, то может быть я и соглашусь. Но из моего опыта, большая часть моих знакомых живет в обычных квартирах, в которых носятся дети, суетится жена, работает телевизор, соседи делают ремонт, про тещу, приходящую в гости, или о ужас, живущую вместе, вообще умолчу. Поэтому, сейчас бурно расцвела тема каворкингов, в которых есть и кофе с печенками, и комната отдыха, и главное уютное рабочее место с тишиной вокруг, как раз и обеспечивающая эту самую творческую обстановку. А иногда творческую обстановку обеспечивают митинги с коллегами, драйв которого ощутить через менсенджер очень сложно.
                3. А аргумент эффективности работы разработчика: «возможность в четверг вечером уехать куда-то за город и вернуться в город только в понедельник вечером», лично у меня рисует в воображении шашлыки, море, природу и свежий воздух, а никак не работу. Люди в большинстве своем — создания слабые, особенно люди творческие. И когда вокруг масса соблазнов отвлечься, они отвлекутся. В лучшем случае будут мучиться угрызением совести, что как раз и вредит ихнему здоровью.
                4. Аргумент: «Иван с Василием работают из дома, почему остальным нельзя? Мы же команда, делающая общее дело!», тоже спорный. Проблема в том, что когда человек получает то, что хотел, он сначала очень старается убедить всех, что получил это не напрасно. И он пыжится, напрягается, всячески оправдывает оказанное ему высокое доверие. Но со временем, все приедается и становится обыденным. Доказывать уже никому ничего не хочется, да и вообще, могло бы быть и получше. А они-то никуда уже без меня не денутся, и пусть только попробуют отобрать завоеванные привилегии и т.д. Так устроен человек –это жизнь.

                Но давайте оставим аргументы в покое и начнем всё-таки анализировать.

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

                Таким образом первая задача, которую мы должны выполнить при создании рабочего места сотрудника – это обеспечение оборудованием и ПО, установленным на него, позволяющим эффективно выполнять выставляемые задачи.

                По моим наблюдениям, чаще всего удаленно- дома работают на своем личном оборудовании, а в офисе, на оборудовании фирмы. И чаще всего, в офисе оборудование мощнее и разнообразнее. Но тут есть в общем-то варианты и разница на мой взгляд несущественная, а сложности вполне преодолимы.

                Теперь об окружении нашего сотрудника:

                1. Руководитель, непосредственно снабжающий работой и обеспечивающий условия для ее выполнения. Их может быть несколько, в зависимости от способа организации управления (линейного, матричного, проектного и т.д.).
                2. Команда соисполнителей. Почти во всех больших проектах необходимо взаимодействовать с другими сотрудниками, производящими совместно один общий продукт.
                3. Заказчик, для которого и разрабатывается программное обеспечение. Чаще всего разработчики изолированы от него, но мы сейчас говорим о команде разработки ПО, а это могут быть и аналитики, и проектные менеджеры и т.п.

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

                Вот тут, опять же на мой взгляд, удаленная работа из дома проигрывает и значительно. Аргументирую:

                1. Руководитель, по моему опыту, чаще всего нуждается в получении непрерывного доступа к ушам сотрудника. Особенно это чувствительно, когда команда работает на субподряде и цепочка получения запросов на работы и откликов на них длинная и требуюет множества согласований и разъяснений. Да и к тому же все сейчас стремятся к клиенториентированности. И ситуация, когда менеджер, выдавший Вам на субподряд работы, хочет получить разъяснения, а в ответ слышит лепетания о том, что человек, который «об этом знает все» сейчас пока не доступен, а когда он будет точно не известно, может вызвать очень негативные последствия.
                2. Как бы я не относился к гибким методологиям, но одно, чего от у них не отнять, это митинги, сплачивающие команду. И проведение их у доски куда эффективнее чем по скайпу. Опять же из моего опыта, я практически каждый день слышу в офисе восклицания программистов: «А кто-то уже делал то-то и то-то» и в большинстве случаев находится, кто-то, кто действительно это делал и готов дать разъяснения. Достичь такого эффекта удаленно невозможно.
                3. Ну, а про общение с клиентом очно или удаленно, думаю вообще ни у кого сомнений не остается в пользу первого.

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

                Ну и наконец, что необходимо получить от человека, работающего на организуемом рабочем месте? Как бы это не банально и цинично звучало: «Как можно больше работы, за как можно меньшие деньги». Но тут следует понимать, что долго так работать, чаще всего не получится. Поэтому приходится обеспечивать сотрудника всякими благами, помогающим ему думать, как можно больше о работе, а не о всяких мирских сложностях. Как я уже говорил выше, современный офис очень сильно превосходит по удобству для работы, практически любое собственное жилье. И в этом случае, на мой взгляд, очень важными является 2 момента:

                • Творческая обстановка в офисе;
                • Постоянное эмоциональное общение с коллегами.

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

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

                Вот моя точка зрения на это вопрос, которая также может быть оспорена.
                Более подробно социальные функции организации, я рассматривал в своей публикации «Социальный Организм — как форма эффективного взаимодействия команды»
                Original source: habrahabr.ru (comments, light).

                https://habrahabr.ru/post/338046/


                Метки:  

                Security Week 37: Дружно выключаем Bluetooth, дыра в Tor на миллион, ботнеты на серверах Elasticsearch

                Пятница, 15 Сентября 2017 г. 19:16 + в цитатник
                Kaspersky_Lab сегодня в 19:16 Разработка

                Security Week 37: Дружно выключаем Bluetooth, дыра в Tor на миллион, ботнеты на серверах Elasticsearch

                  BlueBorne. Запомните это название. Это даже не уязвимость, это — целая пачка дыр в реализациях Bluetooth в Windows, Linux, Android и даже немножко в iOS. Вскрыли этот нарыв исследователи из Armis Labs, они же и оценили число потенциальных жертв в… 5,3 миллиарда устройств.

                  Словом, дело нешуточное. BlueBorne позволяет атаковать девайс с поддержкой Bluetooth с другого «голубозубого» устройства. Причем, оба аппарата не обязательно должны быть спарены. Более того, жертве даже не нужно быть «на радарах» Bluetooth-собратов в округе. Иными словами, если у тебя есть голубой зуб, ты под угрозой.

                  Кулек обнаруженных уязвимостей выглядит так:

                  • CVE-2017-1000251. RCE в ядре Linux;
                  • CVE-2017-1000250. Уязвимость утечки данных в стеке Bluetooth;
                  • CVE-2017-0785. Уязвимость утечки данных в Android;
                  • CVE-2017-0781. RCE в Android;
                  • CVE-2017-0782. RCE в Android;
                  • CVE-2017-0783. Логическая уязвимость в Android (Bluetooth Pineapple);
                  • CVE-2017-8628. Логическая уязвимость в Windows (Bluetooth Pineapple);
                  • Такая лютая, что пока без CVE. RCE-уязвимость проприетарного протокола Apple Low Energy Audio Protocol.

                  Вон те два блютус-ананаса названы так не случайно. Это отсылка к известному хакерскому (официально, конечно, пентестерскому, но мы-то все понимаем;) девайсу Wi-Fi Pineapple. Эту штуку можно оставить в любом публичном месте, где она мимикрирует под любимые домашние сети всех доступных девайсов. Те же простодушно будут цепляться к якобы родным сетям, ну а дальше случится MiTM со всеми вытекающими. Нечто подобное, только через Bluetooth позволяют провернуть CVE-2017-0783 и CVE-2017-8628.

                  В оригинальном исследовании приведена очень живописная картинка с “местами гнездования” уязвимостей в Bluetooth-стеке.



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

                  Парни из Armis Labs поступили ответственно и опубликовали свои открытия лишь после того, как Google, Microsoft и Apple выкатили патчи. Но надо учесть, что баг есть во всех Windows, начиная с Vista, в Linux на ядре 3.3-rc1 с BlueZ (включая Tizen) и в неназванных версиях iOS. Прорва устройств с этими операционками не получат свои патчи никогда.



                  Ну, допустим, смарт-телевизоры в относительной безопасности – их из дома выносят нечасто (хотя как насчет продвинутого соседа за стеной, который не прочь понаблюдать за твоими буднями через веб-камеру?..). А вот за мобильные девайсы как-то совсем тревожно.

                  Zerodium предлагает миллион долларов за зеродей в Tor

                  Новость. Поставщик эксплойтов Zerodium снова объявил красивую семизначную награду за эксплойт нулевого дня – на этот раз в браузере Tor. Полностью функциональный эксплойт для доселе неизвестной уязвимости в Tor под Tails Linux и Windows обойдется конторе в миллион долларов.

                  $250 тыс. будет выплачено за комбинированный эксплойт RCE плюс эскалация привилегий в Tails и Windows, и $200 тыс., если эксплойт окажется только RCE или если он работает только когда включен JavaScript в браузере. Эксплойт принимается с полной документацией, причем вектором атаки обязательно должен быть веб-сайт.

                  Zerodium демонстрирует такую щедрость, очевидно, не за свой счет – есть конкретный заказ. Причем, если верить компании, это заказ от неких госорганов, которые желают добраться до ужасных преступников, скрывающихся при помощи Tor. Государства не названы, но в августе Zerodium уже заявлял о пике запросов от «демократических государств, а также государств, не находящихся под санкциями». То есть круг сужается – это точно не КНДР и не Mother Russia. Так или иначе, а у кого-то серьезно подгорает по поводу Tor, о чем свидетельствуют и суммы, и срок: повышенные баунти действуют только до 30 ноября, так что торопитесь.

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

                  Тысячи серверов Elasticsearch заражены POS-троянцем

                  Новость. Исследование. Исследователь из Kromtech решил прощупать публичные сервера Elasticsearch – свободной поисковой системы. Нашел он там нечто очень любопытное – панель управления POS-троянцами JackPOS и AlinaPOS. Это довольно старая малварь, наделавшая много шуму несколько лет назад.

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



                  Как оказалось, кто-то разжился исходниками этих старых троянцев (AlinaPOS – 2012 год, JackPOS – 2014 год) и выставил на продажу. Соответственно, понадобилась и командная инфраструктура, которую в итоге вскрыли Kromtech. Сканирование при помощи Shodun нашло в Сети 15 тысяч серверов Elasticsearch, четыре тысячи из которых были захвачены POS-ботоводами.

                  Что характерно, почти все хакнутые серверы хостятся в Amazon. Исследователи связывают это с особенностями системы конфигурации серверов. Точнее, сама по себе система достаточно безопасна, а вот пользователи при быстрой установке часто прощёлкивают настройку безопасности – и вот результат.

                  Древности


                  «Pentagon»

                  Опасный вирус, методом «Brain» поражает Boot-сектор флоппи-дисков при обращении к ним. Если при этом дискета уже была поражена вирусом «Brain», то «Pentagon» лечит Boot-сектор этого диска, изменяет его метку и затем заражает своей копией. На заражаемом диске создается файл PENTAGON.TXT. Вирус «выживает» при теплой перезагрузке. Часть вируса зашифрована. Перехватывает int 9 и int 13h. Содержит тексты «© 1987 The Pentagon, Zorell Group», «first sector in segment».

                  Цитата по книге «Компьютерные вирусы в MS-DOS» Евгения Касперского. 1992 год. Страницa 102.

                  Disclaimer: Данная колонка отражает лишь частное мнение ее автора. Оно может совпадать с позицией компании «Лаборатория Касперского», а может и не совпадать. Тут уж как повезет.
                  Original source: habrahabr.ru (comments, light).

                  https://habrahabr.ru/post/338050/


                  Метки:  

                  Как правильно вставлять SVG

                  Пятница, 15 Сентября 2017 г. 19:00 + в цитатник

                  Метки:  

                  Приглашаем на mini ai cups

                  Пятница, 15 Сентября 2017 г. 18:26 + в цитатник
                  sat2707 сегодня в 18:26 Разработка

                  Приглашаем на mini ai cups

                    image


                    Вот уже пять лет мы проводим russian ai cup, крупнейший в СНГ ежегодный чемпионат по искусственному интеллекту (а если проще — по написанию ботов для игр). И вот уже семь лет участники этого чемпионата просят либо оставлять песочницу работать весь год, либо запустить площадку, где можно было бы весь год играться в подобные же конкурсы, только чуть меньше размером.


                    Мы подумали и решили опробовать второй вариант — открыли новую площадку с мини-конкурсами, связанными с искусственным интеллектом и написанием ботов для игр. Встречайте новый для нас класс чемпионатов — http://aicups.ru/.


                    И сразу же предлагаем принять участие в первом, тестовом чемпионате.
                    И не бейте нас, пожалуйста, за дизайн площадки. Мы очень упарывались в качество самого соревнования, поэтому в дизайн мы просто не успели. Сделаем вид, что мы так видим ;)


                    Суть задачи


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


                    1. У каждого пассажира есть вес
                    2. Чем больше пассажиров мы везем, тем медленнее едем
                    3. При достижении определенного (критического) количества пассажиров, мы едем еще медленее

                    Кроме того, задачу мультиплеерная. То есть, у нас есть две группы лифтов и каждый игрок управляет своей группой. Сами же пассажиры делятся на “своих” и “чужих” — чужого сложнее забрать с этажа. Но если смог, получил 2x очков ;)


                    Подробнее обо всем этом можно прочитать ниже, в разделе “Правила”


                    Этапы чемпионата


                    Чемпионат открывается сегодня, 15-сентября, и продлится до 9-го октября.


                    Пока что наш план таков:


                    15-го сентября открывается площадка с песочницей, и у участников появляется возможность загружать свои стратегии и играть не-рейтинговые игры “на интерес” с другими своими стратегиями, с другими игроками или с бейзлайном — в песочнице живут два фейковых пользователя (“good baseline” — забирает всегда только своих пассажиров и “evil baseline” — забирает только чужих).


                    В сутки можно загрузить не более 6 новых решений, и сыграть не более 30 не-рейтинговых игр.


                    18 сентября в песочнице начинают работать рейтинговые игры (их система создает сама). Время от времени псевдослучайно выбирается два участника (для подбора мы используем TrueSkill, спасибо Microsoft за этот прекрасный алгоритм!). От каждого из них мы берем стратегию, которую он до этого выбрал для участия в рейтинге. Две эти стратегии играют друг с другом, победившая передвигается в лидерборде вверх, проигравшая вниз. Таким образом строится рейтинг песочницы.


                    9-го октября мы планируем провести финал. Некоторое количество топовых участников из песочницы будет отобрано в отдельный рейтинг, после чего мы запустим несколько волн игр “каждый с каждым” и таким образом выясним, чья же стратегия самая крутая, раздадим призы, учтем пожелания и пойдем готовиться к следующему подобному чемпионату!


                    Призы


                    Как уже давно принято в наших чемпионатах, мы награждаем TOP6. Расклад призов такой же, как и в прошедшем недавно highloadcup:


                    1. Apple iPad Air 2 Cellular 16GB за первое место
                    2. WD MyCloud 6TB
                    3. WD MyCloud 6TB
                    4. WD MyPassport Ultra 2TB
                    5. WD MyPassport Ultra 2TB
                    6. WD MyPassport Ultra 2TB

                    Кроме того, маечки! Не будем нарушать и эту добрую традицию — TOP20 получат от нас майки с символикой чемпионата. Возможно маек будет больше — в highloadcup мы тоже начали с 20-ти штук, в результате дарим 116 :)


                    Задача и правила


                    Итак, подробнее о задаче.


                    image


                    Место появления пассажиров
                    Лестница
                    Кол-во пассажиров в группе
                    Кол-во пассажиров на этаже
                    Кол-во пассажиров в лифте
                    Тики


                    Описание здания:


                    1. Здание содержит 2 группы лифтов, каждая группа принадлежит одному игроку
                    2. Здание содержит 2 лестницы
                    3. В здании 9 этажей, которые пронумерованы с 1 до 9 снизу вверх
                    4. Высота каждого этажа равна 1
                    5. Расстояние от центра этажа до группы лифтов равно 60 единиц
                    6. Расстояние от центра этажа до места ожидания пассажиров каждой группы равно 20 единиц

                    Лифты:


                    1. Каждая из двух групп содержит 4 лифта
                    2. Лифты в группе отстоят друг от друга на 80 единиц
                    3. В начале игры все лифты стоят на 1 этаже с открытыми дверьми и готовыми к забору пассажиров
                    4. После открытия дверей лифт стоит на этаже минимум 40 тиков
                    5. Открытие дверей длится 100 тиков
                    6. Закрытие дверей длится 100 тиков
                    7. Максимальная вместимость лифта 20 пассажиров
                    8. Пустой лифт преодолевает этаж за 50 тиков
                    9. Лифт едет тем медленнее, чем больше пассажиров он везет
                    10. Если в лифте едет более 4 пассажиров, он теряет скорость в двойном объеме
                    11. Потеря скорости лифта зависит от веса пассажира

                    Пассажиры:


                    1. Пассажиры появляются парами раз в 20 тиков на первом этаже вплоть до 2000 тика
                    2. Каждый пассажир за время игры хочет посетить от 1 до 5 случайных уникальных этажей, кроме первого (распределение количеств этажей и самих этажей равномерное) и вернуться на первый этаж ближе к концу игры
                    3. Пассажиры ждут лифт 500 тиков, после чего уходят на лестницу
                    4. Скорость подъема пассажира по лестнице — один этаж в 200 тиков
                    5. Скорость спуска пассажира по лестнице — один этаж в 100 тиков
                    6. Горизонтальная скорость пассажира 2 единицы в тик
                    7. Пассажир имеет случайный вес от 1 до 1.1
                    8. После выхода из лифта пассажир идет на свой этаж ровно 40 тиков
                    9. Пассажир проводит на этаже 500 тиков, после чего появляется на этаже с той стороны, куда он уходил
                    10. Пассажир всегда идет в ближайший назначенный ему лифт
                    11. Переназначить лифт пассажиру нельзя
                    12. Если лифт пассажира уехал без него, то у пассажира снимается назначенный ему лифт и он возвращается на свою начальную позицию
                    13. Подобрать пассажира противника можно только лифтом, который уже простоял на этаже 40 тиков
                    14. Перемещения лифтовый холл -> лестница, этаж -> лифтовый холл происходят мгновенно

                    Подсчет очков:


                    1. За своего пассажира игрок получает (количество этажей)*10 очков
                    2. За пассажира противника игрок получает (количество этажей)102 очков

                    Побеждает игрок, набравший большее кол-во очков.


                    API и Baseline мы выложили в официальном репозитории с документацией, здесь. В этом же репозитории принимаются новые issue, мы обязательно будем их просматривать.


                    А на чем писать?


                    Сейчас у нас есть API для следующих языков:


                    1. Python 2.7
                    2. Python 3.6
                    3. Cpp11
                    4. Java1.8
                    5. Nodejs
                    6. PHP7

                    Возможно, этот список будет пополняться. Прямо сейчас пытаемся подключить C#.


                    А кому можно участвовать?


                    Участвовать могут все, без ограничений! Заходите, пробуйте! Поучаствовать можно здесь, а обсудить чемпионат можно в VK и в Telegram. Удачи в чемпионате!

                    Original source: habrahabr.ru (comments, light).

                    https://habrahabr.ru/post/337942/


                    Метки:  

                    Поиск сообщений в rss_rss_hh_new
                    Страницы: 1437 ... 1146 1145 [1144] 1143 1142 ..
                    .. 1 Календарь