Phát triển chuỗi khối cho ngành sử dụng Go. Phần 1

Trong bốn tháng nay, tôi đã làm việc trong một dự án có tên “Phát triển các công cụ quản lý và bảo vệ dữ liệu trong chính phủ và các khu vực công nghiệp dựa trên blockchain”.
Bây giờ tôi muốn kể cho bạn nghe về cách tôi bắt đầu dự án này và bây giờ tôi sẽ mô tả chi tiết mã chương trình.

Phát triển chuỗi khối cho ngành sử dụng Go. Phần 1

Đây là bài viết đầu tiên trong loạt bài viết. Ở đây tôi mô tả máy chủ và giao thức. Trên thực tế, người đọc thậm chí có thể viết phiên bản riêng của mình về các thành phần blockchain này.

Và đây là phần thứ hai — về blockchain và cấu trúc dữ liệu giao dịch, cũng như về gói thực hiện tương tác với cơ sở dữ liệu.

Năm ngoái, tại cuộc thi hackathon Đột phá kỹ thuật số, họ đã nảy ra ý tưởng tạo ra một hệ thống hữu ích cho ngành công nghiệp và nền kinh tế kỹ thuật số bằng cách sử dụng công nghệ sổ cái phân tán; một khoản tài trợ cũng đã được cấp cho sự phát triển của Quỹ hỗ trợ đổi mới (tôi nên viết riêng bài viết về khoản tài trợ, dành cho những người mới bắt đầu khởi nghiệp), và bây giờ theo thứ tự.

Quá trình phát triển diễn ra bằng ngôn ngữ Go và cơ sở dữ liệu lưu trữ các khối là LevelDB.
Các phần chính là giao thức, máy chủ (chạy TCP và WebSocket - phần đầu tiên để đồng bộ hóa blockchain, phần thứ hai để kết nối máy khách, gửi giao dịch và lệnh từ JavaScript chẳng hạn.

Như đã đề cập, blockchain này chủ yếu cần thiết để tự động hóa và bảo vệ việc trao đổi sản phẩm giữa nhà cung cấp và khách hàng hoặc cả hai giữa một người. Những người này không vội tin tưởng lẫn nhau. Nhưng nhiệm vụ không chỉ là tạo ra một “sổ séc” với máy tính tích hợp mà còn là một hệ thống tự động hóa hầu hết các công việc thường ngày phát sinh khi làm việc với vòng đời sản phẩm. Mã byte chịu trách nhiệm về vấn đề này, theo thông lệ với các chuỗi khối, được lưu trữ trong đầu vào và đầu ra của giao dịch (bản thân các giao dịch được lưu trữ trong các khối, các khối trong LevelDB được mã hóa trước ở định dạng GOB). Đầu tiên, hãy nói về giao thức và máy chủ (còn gọi là nút).

Giao thức không phức tạp, mục đích chung của nó là chuyển sang chế độ tải một số dữ liệu, thường là khối hoặc giao dịch, để phản hồi một dòng lệnh đặc biệt và nó cũng cần thiết để trao đổi hàng tồn kho, để nút biết đó là ai được kết nối và cách chúng thực hiện công việc (các nút được kết nối cho phiên đồng bộ hóa còn được gọi là “láng giềng” vì IP của chúng được biết và dữ liệu trạng thái của chúng được lưu trữ trong bộ nhớ).

Các thư mục (các thư mục mà Linux gọi chúng) theo cách hiểu của các lập trình viên Go được gọi là các gói, vì vậy ở đầu mỗi tệp có mã Go từ thư mục này họ ghi gói folder_name_where_this_file nằm ở vị trí nào. Nếu không, bạn sẽ không thể cung cấp gói cho trình biên dịch. Chà, đây không phải là bí mật đối với những người biết ngôn ngữ này. Đây là các gói:

  • Truyền thông mạng (máy chủ, máy khách, giao thức)
  • Cấu trúc dữ liệu được lưu trữ và truyền đi (khối, giao dịch)
  • Cơ sở dữ liệu (chuỗi khối)
  • Đoàn kết
  • Máy ảo xếp chồng (xvm)
  • Hiện tại chỉ có phụ trợ (tiền điện tử, loại).

Đây là liên kết đến github

Đây là một phiên bản giáo dục, nó thiếu sự tương tác giữa các quá trình và một số thành phần thử nghiệm, nhưng cấu trúc tương ứng với cấu trúc mà quá trình phát triển đang được thực hiện. Nếu bạn có bất cứ điều gì muốn đề xuất trong phần nhận xét, tôi sẽ sẵn lòng xem xét nó trong quá trình phát triển hơn nữa. Và bây giờ là phần giải thích về máy chủ và giao thức.

Trước tiên hãy nhìn vào máy chủ.

Chương trình con máy chủ hoạt động như một máy chủ dữ liệu chạy trên giao thức TCP bằng cách sử dụng cấu trúc dữ liệu từ gói giao thức.

Quy trình này sử dụng các gói sau: máy chủ, giao thức, loại. Trong gói chính nó tcp_server.go chứa cấu trúc dữ liệu Phục vụ.

type Serve struct {
	Port string
	BufSize int
	ST *types.Settings
}

Nó có thể chấp nhận các tham số sau:

  • Cổng mạng qua đó dữ liệu sẽ được trao đổi
  • Tệp cấu hình máy chủ ở định dạng JSON
  • Cờ để chạy ở chế độ gỡ lỗi (blockchain riêng)

Tiến triển:

  • Đọc cấu hình từ tệp JSON
  • Cờ chế độ gỡ lỗi được chọn: nếu được đặt, bộ lập lịch đồng bộ hóa mạng sẽ không được khởi chạy và chuỗi khối không được tải
  • Khởi tạo cấu trúc dữ liệu cấu hình và khởi động máy chủ

máy chủ

  • Thực hiện việc khởi chạy máy chủ TCP và tương tác mạng theo giao thức.
  • Nó có cấu trúc dữ liệu Serve bao gồm số cổng, kích thước bộ đệm và con trỏ tới cấu trúc loại.Cài đặt
  • Phương thức Run bắt đầu tương tác mạng (lắng nghe các kết nối đến trên một cổng nhất định, khi nhận được kết nối mới, quá trình xử lý của nó sẽ được chuyển sang phương thức xử lý riêng trong một luồng mới)
  • В xử lý dữ liệu từ kết nối được đọc vào bộ đệm, được chuyển đổi thành biểu diễn chuỗi và được chuyển tới giao thức.Lựa chọn
  • giao thức.Lựa chọn trả lại kết quả hoặc gây ra lỗi. kết quả sau đó chuyển sang giao thức.Thông dịchcái nào trả về intrpr - đối tượng thuộc loại Phiên dịchDữ liệu, hoặc gây ra lỗi khi xử lý kết quả lựa chọn
  • Sau đó, chuyển đổi được thực hiện intrpr.Commands[0] kiểm tra một trong: kết quả, inv, lỗi và có một phần mặc định
  • Trong phần kết quả switch được tìm thấy theo giá trị intrpr.Commands[1] kiểm tra các giá trị chiều dài bộ đệm и phiên bản (trong mỗi trường hợp, hàm tương ứng được gọi)

Chức năng Nhận phiên bản и Chiều dài bộ đệm có trong tập tin srvlib.go gói máy chủ

GetVersion(conn net.Conn, version string)

nó chỉ đơn giản là in ra bàn điều khiển và gửi phiên bản được truyền trong tham số tới máy khách:

conn.Write([]byte("result:" + version))

.
Chức năng

BufferLength(conn net.Conn, intrpr *protocol.InterpreteData)

tải một khối, giao dịch hoặc dữ liệu cụ thể khác như sau:

  • In ra bảng điều khiển loại dữ liệu được chỉ định trong giao thức cần được chấp nhận:
    fmt.Println("DataType:", intrpr.Commands[2])
  • Đọc giá trị intrpr.Body đến một biến số buf_len
  • Tạo bộ đệm newbuf kích thước quy định:
    make([]byte, buf_len)
  • Gửi phản hồi ok:
    conn.Write([]byte("result:ok"))
  • Hoàn toàn lấp đầy bộ đệm từ luồng đọc:
    io.ReadFull(conn, newbuf)

    .

  • In nội dung của bộ đệm ra bàn điều khiển
    fmt.Println(string(newbuf))

    và số byte được đọc

    fmt.Println("Bytes length:", n)
  • Gửi phản hồi ok:
    conn.Write([]byte("result:ok"))

Các phương thức từ gói máy chủ được cấu hình để xử lý dữ liệu nhận được bằng các chức năng từ gói giao thức.

Nghị định thư

Một giao thức đóng vai trò như một phương tiện thể hiện dữ liệu trong trao đổi mạng.

Choice(str string) (chuỗi, lỗi) thực hiện xử lý chính dữ liệu mà máy chủ nhận được, nhận chuỗi biểu diễn dữ liệu dưới dạng đầu vào và trả về một chuỗi được chuẩn bị cho Thông dịch viên:

  • Chuỗi đầu vào được chia thành phần đầu và phần thân bằng cách sử dụng ReqParseN2(str)
  • head được chia thành các phần tử và được đặt vào một lát lệnh bằng cách sử dụng ReqParseHead(head)
  • В switch(lệnh[0]) chọn lệnh nhận được (cmd, khóa, địa chỉ hoặc phần được kích hoạt mặc định)
  • 2 lệnh được kiểm tra trong cmd switch(lệnh[1]) - chiều dài и sự chán ghét.
  • chiều dài kiểm tra kiểu dữ liệu trong lệnh[2] và lưu nó vào loại dữ liệu
  • Kiểm tra điều đó thân hình chứa một giá trị chuỗi
    len(body) < 1
  • Trả về chuỗi phản hồi:
    "result:bufferlength:" + datatype + "/" + body
  • sự chán ghét trả về một chuỗi
    return "result:version/auto"

Thông dịch viên

Chứa cấu trúc InterpreteData và thực hiện xử lý thứ cấp dữ liệu được trả về từ Sự lựa chọn chuỗi và sự hình thành đối tượng Phiên dịchDữ liệu.

type InterpreteData struct {
	Head string
	Commands []string
	Body string
	IsErr bool
	ErrCode int 
	ErrMessage string
}

Chức năng

Interprete(str string) (*InterpreteData, error)

chấp nhận một chuỗi kết quả và tạo và trả về một tham chiếu đến đối tượng Phiên dịchDữ liệu.

Tiến triển:

  • Tương tự Sự lựa chọn đầu và thân được chiết xuất bằng cách sử dụng ReqParseN2(str)
  • đầu được chia thành các phần tử bằng cách sử dụng ReqParseHead(đầu)
  • Đối tượng được khởi tạo Phiên dịchDữ liệu và một con trỏ tới nó được trả về:

res := &InterpreteData{
	Head: head,
	Commands: commands,
	Body: body,
}
return res, nil

Đối tượng này được sử dụng trong máy chủ.go gói chính.

Khách hàng

Gói client chứa các chức năng TCPKết nối и TCPResponseDữ liệu.

Chức năng

TCPConnect(s *types.Settings, data []byte, payload []byte)

hoạt động như sau:

  • Một kết nối được thực hiện với kết nối được chỉ định trong đối tượng cài đặt đã thông qua
    net.Dial("tcp", s.Host + ":" + s.Port)
  • Dữ liệu được truyền trong tham số data được truyền đi:
    conn.Write(data)
  • Câu trả lời đã được đọc
    resp, n, _ := TCPResponseData(conn, s.BufSize)

    và in trên bảng điều khiển

    fmt.Println(string(resp[:n]))
  • Nếu được chuyển khối hàng sau đó chuyển nó đi
    conn.Write(payload)

    và cũng đọc phản hồi của máy chủ, in nó ra bảng điều khiển

Chức năng

 TCPResponseData(conn net.Conn, bufsiz int) ([]byte, int, error)

tạo một bộ đệm có kích thước được chỉ định, đọc phản hồi của máy chủ ở đó và trả về bộ đệm này cũng như số byte đã đọc cũng như đối tượng lỗi.

Chương trình con của khách hàng

Dùng để gửi lệnh đến các máy chủ nút, cũng như lấy số liệu thống kê và kiểm tra ngắn gọn.

Có thể chấp nhận các tham số sau: tệp cấu hình ở định dạng JSON, dữ liệu được gửi đến máy chủ dưới dạng chuỗi, đường dẫn đến tệp được gửi tới payload, cờ mô phỏng bộ lập lịch nút, loại dữ liệu được truyền dưới dạng giá trị số.

  • Lấy cấu hình
    st := types.ParseConfig(*config)
  • Nếu cờ emu được thông qua, nó sẽ bắt đầu người đổ vỏ
  • Nếu cờ f chỉ đường dẫn đến tệp được cung cấp thì chúng tôi sẽ tải dữ liệu của nó vào fdb và nội dung được gửi đến máy chủ
    client.TCPConnect(st, []byte(CMD_BUFFER_LENGTH + ":" + strconv.Itoa(*t) + "/" + strconv.Itoa(fdblen)), fdb)
  • Nếu tệp không được chỉ định thì dữ liệu từ cờ sẽ được gửi đơn giản -d:
    client.TCPConnect(st, []byte(*data), nil)

Tất cả điều này là một biểu diễn đơn giản thể hiện cấu trúc của giao thức. Trong quá trình phát triển, chức năng cần thiết sẽ được thêm vào cấu trúc của nó.

Trong phần thứ hai tôi sẽ nói về cấu trúc dữ liệu cho các khối và giao dịch, trong phần 3 về máy chủ WebSocket để kết nối từ JavaScript, trong phần 4 tôi sẽ xem xét bộ lập lịch đồng bộ hóa, sau đó là máy xếp chồng xử lý mã byte từ đầu vào và đầu ra, mật mã và nhóm cho đầu ra.

Nguồn: www.habr.com

Thêm một lời nhận xét