- PVSM.RU - https://www.pvsm.ru -
[1]
В этой части я дойду до момента, когда пришла смс-ка «Не звони и не пиши мне больше!!!!»
Первая часть тут [2], вторая тут [3].
4,5 года назад я имел неосторожность начать писать свою криптовалюту на совсем неподходящем для этого дела языке — на PHP. В итоге, конечно, написал (я упрямый), но получился костыль на костыле и то, что оно вообще работало было просто какой-то магией.
Сразу хочу предупредить, программер я самоучка-недоучка и пишу код, мягко сказать, неидеально.
Началось всё с того, что я расстался с девушкой, по имени Катя и в этот же день (4 апреля 2015-го) решил изучить Go и переписать свою криптовалюту. Писать про Катю не под спойлерами не могу, т.к. хабр всё же для IT-шных статей, а не для любовных рассказов и суровые айтишники, которым интересна тема Go, могут просто не обращать внимание на спойлеры «про Катю».
Итог 8 месяцев: приложение работает на Win (64 [4]/32 [5]), OSX(64 [6]/32 [7]), Linux(64 [8]/32 [9]), FreeBSD(64 [10]/32 [11]), Android [12], IOS [13] (будет круто, если кто-то закинет в App Store).
Общего кода ~73к строк, кода под разные ОС где-то несколько сотен строчек.
40к — обработка/генерация блоков/тр-ий, 17.5к — контроллеры для интерфейса, 15.5к — шаблоны
Поддерживаются PostgreSQL, SQLite, MySQL.
Тех, кто будет тестировать мое творение, предупреждаю — могут быть баги, и если у Вас есть время, чиркните о них, пожалуйста, на darwin@dcoin.club [14] или в личку на хабре. Пожелания и советы тоже приветствуются.
В первых двух частях я рассказал про то, как в dcoin функционирует веб-сервер и про html/template [15].
В этой статье я расскажу про базы данных, плавное завершение приложения, шифрование и парсинг блоков
В dcoin я сделал поддержку sqlite, postgres, mysql. Для обычных десктопных приложений по умолчанию выбрана sqlite, т.к. не нужно ничего ставить дополнительно. Тем, кто будет поднимать свой dcoin-пул лучше выбирать между postgres и mysql.
За работу с SQL базами данных в golang отвечает пакет database/sql [16]. К нему нужно подключать драйвер БД sqlite [17], postgresql [18], mysql [19].
Вначале я использовал только sqlite, затем решил подключить postgresql и mysql. Оказалось не очень сложно. Изменить пришлось только параметры для подключения к БД в sql.Open() и учесть различия в синтаксисе Sql-запросов
Тут [20] моя реализация подключения к БД.
Обертки для работы с БД я не использовал, т.к. голые sql-запросы мне было удобнее переносить из старой версии Dcoin, по этой же причине в коде есть довольно убогие конструкции с преобразованием типов данных, полученных из БД.
Пара грабель, на которые я наступил:
1. Если забыть после db.Query(«sql») вызвать rows.Close(), то будет куча незакрытых коннектов.
2. defer нельзя вызывать до обработки ошибки, т.к. если будет ошибка, то row будет nil, а вызов rows.Close() приведет к панике. Вот так делать нельзя:
rows, err := db.Query("SELECT * FROM table")
defer rows.Close()
if err != nil {
return err
}
Вот так верно:
rows, err := db.Query("SELECT * FROM table")
if err != nil {
return err
}
defer rows.Close()
type minersDataType struct {
hosts string `json:"hosts"`
}
func main() {
result_ := minersDataType{hosts: "pool.dcoin.club"}
result, _ := json.Marshal(result_)
}
result будет содержать "{}"
Если заменить hosts на Hosts, то всё будет работать и в result появится {«hosts»:«111111»}
type minersDataType struct {
Hosts string `json:"hosts"`
}
func main() {
result_ := minersDataType{Hosts: "pool.dcoin.club"}
result, _ := json.Marshal(result_)
}
Я с этим долго тупил, поэтому делаюсь инфой, чтобы кто-то не наступил на те же грабли
Если в процессе парсинга блоков выйти из программы, то данные могут быть занесены не полностью и произойдет рассинхронизация. Например, транзакция по начислению средств обработается, а по списанию — нет. Поэтому важно плавно завершить работу, если поступил сигнал о закрытии приложения
SigChan = make(chan os.Signal, 1)
C.waitSig() // про это напишу ниже
go func() {
signal.Notify(SigChan, os.Interrupt, os.Kill, syscall.SIGTERM)
// ждем, пока придет сигнал
<-SigChan
// сообщаем демонам, что надо срочно завершать работу
for i := 0; i < countDaemons; i++ {
daemons.DaemonCh <- true
answer := <-daemons.AnswerDaemonCh
}
// демоны завершили работу, теперь можно закрыть соединение с БД
DB.Close()
}()
C.waitSig() — это нужно для windows, т.к. сигналы в windows Go не видит.
/*
#include <stdio.h>
#include <signal.h>
extern void go_callback_int();
static inline void SigBreak_Handler(int n_signal){
go_callback_int();
}
static inline void waitSig() {
#if (WIN32 || WIN64)
signal(SIGBREAK, &SigBreak_Handler);
signal(SIGINT, &SigBreak_Handler);
#endif
}
*/
import (
"C"
)
//export go_callback_int
func go_callback_int() {
SigChan <- syscall.Signal(1)
}
Если приходит SIGBREAK или SIGINT, то вызывается сишный SigBreak_Handler, который вызывает go_callback_int, который шлет в канал SigChan инфу, что был сигнал о завершении.
В Dcoin обработка сигналов реализована тут [21]
Мне понадобилось зашифровать при помощи AES ключ в Go, а расшифровать в JS. Мучился где-то 2 дня. Оказывается IV [22] надо передавать вместе с самим зашифрованным текстом. У меня эта функция находится тут [23]
func Encrypt(password, text []byte) ([]byte, error) {
iv := []byte(RandSeq(aes.BlockSize))
c, err := aes.NewCipher(password)
if err != nil {
return nil, ErrInfo(err)
}
plaintext := PKCS5Padding([]byte(text), c.BlockSize())
cfbdec := cipher.NewCBCEncrypter(c, iv)
EncPrivateKeyBin := make([]byte, len(plaintext))
cfbdec.CryptBlocks(EncPrivateKeyBin, plaintext)
EncPrivateKeyBin = append(iv, EncPrivateKeyBin...)
return EncPrivateKeyBin, nil
}
и расшифровка в JS:
ivAndText = atob(text);
iv = ivAndText.substr(0, 16);
encText = ivAndText.substr(16);
cipherParams = CryptoJS.lib.CipherParams.create({
ciphertext: CryptoJS.enc.Base64.parse(btoa(encText))
});
password = CryptoJS.enc.Latin1.parse(hex_md5(password));
var decrypted = CryptoJS.AES.decrypt(cipherParams, password, {mode: CryptoJS.mode.CBC, iv: CryptoJS.enc.Utf8.parse(iv), padding: CryptoJS.pad.Iso10126 });
var decryptedText = hex2a(decrypted.toString());
Расшифровку я делаю в worker-е [24], иначе из-за синхронности ненадолго вешается браузер.
Непосредственно за разбор блоков и занесение данных в таблицы отвечает пакет dcparser [25]
Рассмотрим например, транзакцию регистрации нового юзерского ключа, по сути нового Dcoin-пользователя — new_user.go [26]
NewUserInit отвечает за заполнение переменных, которые содержат данные этой транзакции. Тут их всего две — public_key и sign
NewUserFront отвечает за проверку данных. В частности, проверяет, является пользователь, который сгенерировал эту транзакцию майнером. Проверяет, нет ли превышения лимитов и пр.
NewUser заносит данные в БД
NewUserRollbackFront откатывает изменения, которые были занесены методом NewUserFront
NewUserRollback откатывает данные, которые были занесены методом NewUser
Аналогичные 4 метода есть для каждого из 70-и типов транзакций.
На минуточку представьте как парсятся > 270 000 блоков. 70 методов заносят данные в 180 таблиц и 1100 разных колонок. А затем то, что меня завораживает больше всего — откат всех данных по одному блоку в обратном порядке через Rollback и RollbackFront. Такой полный откат делается для тестирования корректности работы dcparser. Если где-то как-то неверно занеслись данные, то это сразу станет видно.
В следующих статьях я расскажу про то, как я немного изменил gomobile, добавив уведомления и работу в фоне для IOS и Android приложений.
Автор: c-darwin
Источник [1]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/go/109015
Ссылки в тексте:
[1] Image: http://habrahabr.ru/post/274885/
[2] тут: http://habrahabr.ru/company/dcoin/blog/272695/
[3] тут: http://habrahabr.ru/post/273333/
[4] 64: https://github.com/c-darwin/dcoin-go/releases/download/v2.0.1b4/dcoin_win64.exe
[5] 32: https://github.com/c-darwin/dcoin-go/releases/download/v2.0.1b4/dcoin_win32.exe
[6] 64: https://github.com/c-darwin/dcoin-go/releases/download/v2.0.1b4/dcoin_osx64.dmg
[7] 32: https://github.com/c-darwin/dcoin-go/releases/download/v2.0.1b4/dcoin_osx32.dmg
[8] 64: https://github.com/c-darwin/dcoin-go/releases/download/v2.0.1b4/dcoin_linux64.deb
[9] 32: https://github.com/c-darwin/dcoin-go/releases/download/v2.0.1b4/dcoin_linux32.deb
[10] 64: https://github.com/c-darwin/dcoin-go/releases/download/v2.0.1b4/dcoin_freebsd64.zip
[11] 32: https://github.com/c-darwin/dcoin-go/releases/download/v2.0.1b4/dcoin_freebsd32.zip
[12] Android: https://github.com/c-darwin/dcoin-go/releases/download/v2.0.1b4/dcoin.apk
[13] IOS: http://dcoin.club/ru/ios.html
[14] darwin@dcoin.club: mailto:darwin@dcoin.club
[15] html/template: https://golang.org/pkg/html/template/
[16] database/sql: https://golang.org/pkg/database/sql/
[17] sqlite: http://github.com/mattn/go-sqlite3
[18] postgresql: http://github.com/lib/pq
[19] mysql: http://github.com/go-sql-driver/mysql
[20] Тут: https://github.com/c-darwin/dcoin-go/blob/master/packages/utils/sql.go#L51
[21] тут: https://github.com/c-darwin/dcoin-go/blob/master/packages/stopdaemons/notmobile.go#L45
[22] IV: https://ru.wikipedia.org/wiki/%D0%A0%D0%B5%D0%B6%D0%B8%D0%BC_%D1%88%D0%B8%D1%84%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F#Initialization_vector_.28IV.29
[23] тут: https://github.com/c-darwin/dcoin-go/blob/master/packages/utils/utils.go#L2839
[24] worker-е: https://github.com/c-darwin/dcoin-go/blob/master/static/js/worker.js#L37
[25] dcparser: https://github.com/c-darwin/dcoin-go/tree/master/packages/dcparser
[26] new_user.go: https://github.com/c-darwin/dcoin-go/blob/master/packages/dcparser/new_user.go
Нажмите здесь для печати.