dotzero

↑ ↑ ↓ ↓ ← → ← → B A Start

Pad - pastebin без стероидов

В то время когда к интернету подключена каждая кофеварка, sharing небольших текстовых данных между MacOS и Linux или между iPhone и Windows все еще превращается в «танцы с бубнами». Можно конечно отправить письмо самому себе, как в средневековье. А если получатель кто-то другой, то на сцену выходит Pastebin со своими непомерными требованиями к заголовкам, типу контента, уровню видимости и капчей. Можно попроще?

Встречайте Pad — сервис где не нужно ничего заполнять, выбираешь ссылку и вставляешь текст в textarea и открываешь ссылку на другом устройстве. Сервис, как сейчас принято, написан на Go, а для стораджа используется BoltDb, это такая встраиваемая key/value база данных. Завернуто это, как полагается, в Docker, куда уж без него. Исходники можно посмотреть на Github.

Книги о Биткоине и не только

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

«Эпоха криптовалют», Пол Винья

Рассуждения о природе денег, прогнозы о возможном влиянии криптовалют на экономику и пара заметок о сомнительных стартапах. Авторы «Эпохи криптовалют» – обозреватели Wall Street Journal сделали все, чтобы ни у кого не было желания дочитать книгу до конца. А отсутствие технических деталей и минимальное погружение в хронологию событий делает книгу не стоящей времени потраченного на чтение.

«Биткоин. Графический роман о криптовалюте»

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

«Цифровое Золото», Натаниэль Поппер

Хронология событий связанных с Биткоином в период между 2008 и 2014 годами, начиная с Адама Бэка и Хэла Финни и заканчивая падением Mt.Gox и Silkroad. Истории органично переплетены, и рассказывают о ключевых личностях стоявших у истоков протокола, первых майнерах и первых инвесторах. В книге нет технических деталей но отсутствие таковых даже идет ей на пользу. Однозначно, одна из моих любимых книг в 2017 году.

«Mastering Bitcoin», Andreas M. Antonopoulos

Алгоритмы, спецификации и как протокол работает изнутри. Мало воды и много примеров кода на c++ и python. Абсолютный must read для как разработчиков, так и просто любопытных. Книга выпущена под лицензией Creative Commons и доступна для публичного скачивания на русском языке.

«Киберпреступник №1. История создателя подпольной сетевой империи», Ник Билтон

История Росса Ульбрихта и сайта Silk Road как захватывающий детектив от начала и до конца. Особенно интересно читать и паралельно изучать огромный архив документов связанных с делом Silk Road. Росс не давал интервью при написании книги, так что некоторые из его поступков описаны по косвенным признакам, а его внутренний кофликт можно списать на фантазии автора.

Релизим Go приложения на Github

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

  • Сборка бинарников под Linux/Mac, с установкой значений для ldflags флагов;
  • Автоматическая генерация описания релиза из сообщений к коммитам;
  • Единообразие - чтобы не собирать релиз из палок и веревок.

Вооружившись списком идем на Github в поисках подходящего инструмента.

aktau/github-release

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

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

$ github-release release \
    --user aktau \
    --repo gofinance \
    --tag v0.1.0 \
    --name "the wolf of source street" \
    --description "Not a movie, contrary to popular opinion. Still, my first release!" \

$ github-release upload \
    --user aktau \
    --repo gofinance \
    --tag v0.1.0 \
    --name "gofinance-osx-amd64" \
    --file bin/darwin/amd64/gofinance

tcnksm/ghr

Сам автор сравнивает свой инструмент с предыдущим кандидатом говоря, что его поделка еще проще. Местами он проще и умеет читать из файла .git/config название репозитория для создания релиза, а еще умеет заливать все файлы разом в параллель. На этом плюсы заканчиваются, сборка релиза и описание остаются без внимания.

$ ghr -b "Release description" v0.1.0 pkg/

c4milo/github-release

Похож на ghr но не умеет читать настройки из git. Единственный из претендентов кто решил помочь советом как из gox и Makefile сделать готовый релиз и залить на Github, собрав при этом описание из сообщений к коммитам.

$ gox -ldflags "-X main.Version=v0.1.0" \
    -osarch="darwin/amd64" \
    -os="linux" \
    -output "dist/{{.Dir}}_$(VERSION)_{{.OS}}_{{.Arch}}/$(NAME)" \
    ./...

$ github-release \
    c4milo/release \
    v0.1.0 \
    "$$(git rev-parse --abbrev-ref HEAD)" \
    "**Changelog**<br/>$$(git log $$comparison --oneline --no-merges)" \
    "dist/*";

goreleaser/goreleaser

Просто комбайн, умеет много и при этом тщательно это скрывает. Редкий случай когда такой монстр может быть таким простым в управлении. Конфигурация по сборке хранится в файле .goreleaser.yml, а потому для сборки флагами можно пренебречь. Умеет собирать, запаковывать и заливать билды под все платформы, генерить описание из сообщений к коммитам и даже делать формулы для Homebrew.

Но есть и минусы - излишня любовь к саморекламе, описания к релизам будут содержать подпись «Automated with GoReleaser…». А еще у него сносит башню если текущий git HEAD не указывает на тег с номеров версии. Но если смириться с этим, то вариантов выбирать что-то отличное от goreleaser просто нет.

Ledger HW.1

На волне криптохайпа захотелось приобрести себе аппаратный Bitcoin кошелек. Но прикинув цены на Trezor и Ledger Nano S купил младшую модель Ledger HW.1 за $20, пользоваться я им конечно не буду.

Главная фишка аппаратных кошельков хранение приватных ключей без подключения к интернету и апаратная рандомизация seed при создании ключа. Большинство из них имеют экран для отображения информации о транзакциях и коды вторых факторов при подключении к компьютеру, Ledger HW.1 конечно не имеет.

Ledger HW.1

В упаковке помимо usb стика лежала карта второго фактора авторизации и бумажка для записи мнемонической фразы от кошелька. Вместо инструкции по применению лежала памятка с адресом https://www.ledgerwallet.com/start.

Ledger HW.1

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

Поскольку на сайте Ledger кроме красивых картинок невозможно найти список коинов, которые поддерживает HW.1, то второе разочарование постигло когда подключенный стик просил выбрать Bitcoin или Bitcoin Cash и оказалось, что ничего другого он не умеет, а все разнообразие альтоинов поддерживает только более продвинутая (читай — дорогая) линейка кошельков. Выбираем Bitcoin segwit2x, вводим второй фактор с карточки и кошелек готов к работе.

Ledger HW.1

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

Имплементация LRU кэша на Go

LRU: Least Recently Used — алгоритм кэширования, при котором вытесняются значения, которые дольше всего не запрашивались. Алгоритмическая сложность O(1), а потому кеш работает очень быстро и используется в memcached.

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

Свой memcached на Go

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

import (
    "container/list"
)

type Item struct {
    Key   string
    Value interface{}
}

type LRU struct {
    capacity int
    items    map[string]*list.Element
    queue    *list.List
}

func NewLru(capacity int) *LRU {
    return &LRU{
        capacity: capacity,
        items:    make(map[string]*list.Element),
        queue:    list.New(),
    }
}

Структура LRU содержит поле с количеством ячеек, поле двусвязного списка *list.List и поле для хранения хеш-таблицы map[string]*list.Element. А Item содержит поля для хранения ключа и значения кэшируемого элемента. Функция конструктор NewLru инициализирует LRU и возвращает ссылку на экземпляр.

Сохраняем значение в кэше

При сохранении элемента в кеше, инициализируем новую структуру Item и добавляем ее в начало очереди c.queue.PushFront(item). Возвращенный очередью *Element добавляем в хеш-таблицу, где ключ это идентификатор записи, а значение это ссылка на элемент очереди.

func (c *LRU) Set(key string, value interface{}) bool {
    if element, exists := c.items[key]; exists == true {
        c.queue.MoveToFront(element)
        element.Value.(*Item).Value = value
        return true
    }

    if c.queue.Len() == c.capacity {
        c.purge()
    }

    item := &Item{
        Key:   key,
        Value: value,
    }

    element := c.queue.PushFront(item)
    c.items[item.Key] = element

    return true
}

Перед добавлением в очередь проверяем нет ли уже такого ключа в хеш-таблице и если есть, то заменяем значение на новое и двигаем в начало очереди c.queue.MoveToFront(element).

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

func (c *LRU) purge() {
    if element := c.queue.Back(); element != nil {
        item := c.queue.Remove(element).(*Item)
        delete(c.items, item.Key)
    }
}

Получаем значение из кэша

При запросе элемента из кеша, ищем соответствие ключа в хеш-таблице и при нахождении получаем значение элемента через каст значения на структуру element.Value.(*Item).Value. Перед возвращением перемещаем элемент в начало очереди.

func (c *LRU) Get(key string) interface{} {
    element, exists := c.items[key]
    if exists == false {
        return nil
    }
    c.queue.MoveToFront(element)
    return element.Value.(*Item).Value
}

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