📜 ⬆️ ⬇️

As I rewrote Dcoin on Go for 8 months ... about Katya, in general


In this part, I will go to the moment when the sms came, "Do not call and do not write me more !!!!"

The first part is here , the second is here .
4.5 years ago I had the imprudence to start writing my cryptocurrency in a completely inappropriate language for this business - in PHP. As a result, of course, I wrote (I am stubborn), but it turned out to be a crutch on a crutch and that it worked at all was just some kind of magic.
I just want to warn you, I’m a self-taught programmer and I write code, to say the least, imperfectly.

It all started with the fact that I broke up with a girl named Katya and on the same day (April 4, 2015) decided to study Go and rewrite my cryptocurrency. I can’t write about Katya not under the spoilers; Habr is still for IT-related articles, and not for love stories and harsh IT people who are interested in Go, can simply not pay attention to the spoilers “about Katya”.

A total of 8 months: the application runs on Win ( 64/32 ), OSX (64/32), Linux (64/32), FreeBSD (64/32), Android , IOS (it will be cool if someone throws on the App Store ).
General code ~ 73k lines, code for different OS somewhere a few hundred lines.
40k - processing / generation of blocks / tr-y, 17.5k - controllers for the interface, 15.5k - templates
PostgreSQL, SQLite, MySQL are supported.
')
Those who will test my creation, I warn you - there may be bugs, and if you have time, drop a line about them, please, at darwin@dcoin.club or in a personal on Habré. Suggestions and advice are also welcome.

In the first two parts, I talked about how a dcoin web server and html / template function.

In this article I will talk about databases, smooth application termination, encryption and parsing blocks.

About Katya
All night, neither I nor she could fall asleep, just lay silently. In my head there were thoughts “see her, find another, she is inadequate.” What she thought, I do not know.
In the morning, for some reason, I again felt love for her. I fed breakfast and packed the soup in a container so that Katya ate at work.

Database


In dcoin, I did support sqlite, postgres, mysql. For normal desktop applications, sqlite is selected by default. do not need to put anything extra. Those who will raise their dcoin pool should choose between postgres and mysql.
The database / sql package is responsible for working with SQL databases in golang. You need to connect the sqlite , postgresql , mysql database driver to it.

At first I only used sqlite, then decided to connect postgresql and mysql. It turned out not very difficult. I had to change only the parameters for connecting to the database in sql.Open () and take into account the differences in the syntax of Sql queries
Here is my implementation of the connection to the database.
I did not use wrappers for working with the database, since It was more convenient for me to transfer bare sql queries from the old version of Dcoin, for the same reason there are some rather miserable constructs in the code converting data types obtained from the database.

The rake pair that I stepped on:
1. If you forget to call rows.Close () after db.Query ("sql"), then there will be a bunch of unclosed connections.
2. defer can not be called before handling errors, because if there is an error, row will be nil, and the rows.Close () call will panic. You can't do this:

rows, err := db.Query("SELECT * FROM table") defer rows.Close() if err != nil { return err } 

That's right:
 rows, err := db.Query("SELECT * FROM table") if err != nil { return err } defer rows.Close() 

About Katya
Then she again transferred the meeting many times. I do not know why, but every day I fell in love with her more and more. I just wanted her, I only thought about her. At some point I caught myself thinking that I wanted to marry her. I accidentally found Katya’s poems on the stihi.ru website, after one verse I even shed tears, it was about how her former guys treated her badly. I went crazy, this feeling that I felt for her was strange and not clear to me, I have never had such a state.

Json


 type minersDataType struct { hosts string `json:"hosts"` } func main() { result_ := minersDataType{hosts: "pool.dcoin.club"} result, _ := json.Marshal(result_) } 

result will contain "{}"
If replacing hosts with Hosts, then everything will work and in the result {hosts will appear: "111111"}
 type minersDataType struct { Hosts string `json:"hosts"` } func main() { result_ := minersDataType{Hosts: "pool.dcoin.club"} result, _ := json.Marshal(result_) } 

I was stupid with this for a long time, so I’m doing info so that someone doesn’t step on the same rake

About Katya
I wrote Kate that I love her, I want to be only with her. She continued to carry on dating anyway. Tired, I bought her favorite flowers and went to see her. For some reason, even learned one of her poems. Arrived at home no one. The phone is not responding.

Signals


If in the process of parsing blocks to leave the program, the data may not be fully recorded and will be out of sync. For example, a transaction for the accrual of funds will be processed, but for write-off - not. Therefore, it is important to smoothly shut down if there is a signal to close the application.
 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 () - this is needed for windows, because Signals in windows Go does not see.
 /* #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) } 

If SIGBREAK or SIGINT arrives, then the sigBreak_Handler is called, which calls go_callback_int, which sends information to the SigChan channel, which was a signal of completion.
In Dcoin, signal processing is implemented here.

About Katya
I wrote to her at VK, said that she had forgotten the bodies at home, and now she was with a friend. I wrote that I would wait for her. Then he received "Do not call and do not write me more !!!!". He didn’t ask any questions, called the next apartment, asked me to send the flowers to Kate when she was home. I came home, a few hours later I decided to rewrite Dcoin to Go.

Encryption


I needed to encrypt the key in Go using AES, and decrypt it in JS. I suffered about 2 days. It turns out IV must be transferred along with the encrypted text itself. I have this feature here

 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 } 

and decoding in 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()); 

I do the decryption in the worker , otherwise, because of the synchronicity, the browser hangs for a while.

Block parsing


The dcparser package is responsible for parsing the blocks and entering the data into the tables .
Consider, for example, the registration transaction of a new user key, in fact a new Dcoin user, new_user.go
NewUserInit is responsible for filling in the variables that contain the data of this transaction. There are only two of them - public_key and sign
NewUserFront is responsible for data validation. In particular, it checks if the user who generated this transaction is a miner. Checks if there are no exceeded limits, etc.
NewUser enters the data in the database
NewUserRollbackFront rolls back the changes that were made by the NewUserFront method
NewUserRollback rolls back the data that was entered by the NewUser method
There are 4 similar methods for each of the 70 types of transactions.
For a moment, imagine how parsitsya> 270 000 units. 70 methods enter data in 180 tables and 1100 different columns. And then the thing that fascinates me the most is the rollback of all data on one block in the reverse order through Rollback and RollbackFront. Such a full rollback is done to test the correctness of dcparser. If the data somehow incorrectly entered the data, it will be immediately apparent.

About Katya
A couple of days later I wrote to her that I was leaving for several months with my head in my project and asked me not to bother me over nothing. A week later, Kati came sms-ka.

Conclusion


In the following articles, I will talk about how I slightly changed the gomobile by adding notifications and working in the background for IOS and Android applications.

Source: https://habr.com/ru/post/274885/


All Articles