Go行业的区块链开发。第1部分

四个月以来,我一直在从事一个名为“在政府和行业中开发基于区块链的安全和数据管理工具的项目”。
现在我想谈谈我是如何开始这个项目的,现在我将详细描述程序代码。

图片

这是系列文章中的第一篇。在这里,我描述了服务器和协议。实际上,读者甚至可以编写自己的这些区块链元素版本。

但是第二部分是关于区块链和交易的数据结构,以及与数据库实现交互的包。

去年,在Digital Break黑客马拉松上,他们提出了一个想法,即为了使行业和数字经济成为分布式注册表技术的有用系统,创新援助基金会也为开发提供了一笔赠款(关于赠款,我应该为刚开始创业的人写一篇单独的文章。 ),现在按顺序进行。

开发是在Go中进行的,存储块的数据库是LevelDB。
主要部分是协议,服务器(运行TCP和WebSocket)-例如,第一部分用于同步区块链,第二部分用于连接客户端,发送来自JavaScript的事务和命令。

如前所述,主要需要使用此区块链来自动化和保护供应商和客户之间或两者之间的产品交换。他们不急于彼此信任。但是,任务不仅是用内置的计算器制作“支票簿”,而且是一个在产品生命周期中自动执行大多数日常任务的系统。按照区块链的惯例,负责该业务的字节码存储在交易的输入和输出中(交易本身在块中,LevelDB中的块以GOB格式预编码)。首先,让我们谈谈协议和服务器(又名节点)。

该协议并不是很有效,它的全部意义在于,响应特殊的命令行,切换到某些数据(通常是块或事务)的加载模式,并且交换库存也需要它,以便节点知道它与谁连接以及如何连接。它们有事要做(为同步会话连接的节点也称为“邻居”,因为它们的IP是已知的,并且其状态数据存储在内存中)。

文件夹(Linux称为目录的文件夹)在理解Go-programmers时被称为包,因此在每个文件的开头(从该目录开始使用Go-code),它们都将编写文件夹package_name_where_sit_this_file。否则,程序包将无法提供给编译器。好吧,对于那些懂这种语言的人来说,这不是秘密。这些软件包是:

  • 网络(服务器,客户端,协议)
  • 存储和传输的数据的结构(块,事务)
  • (blockchain)
  • (consensus)
  • (xvm)
  • (crypto, types) .

这是github的链接,

这是一个培训版本,它缺少进程间的通信,并且具有几个实验组件,其结构与正在开发的组件相对应。如果您在评论中有话要说,我将很乐意考虑进一步的发展。现在用于服务器和协议

首先,看一下服务器。

服务器例程使用协议包中的数据结构充当在TCP协议之上运行的数据服务器。

该例程使用以下软件包:serverprotocoltypes该软件包本身包含tcp_server.go数据结构即成

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

它可以采用以下参数:

  • 通过其交换数据的网络端口
  • JSON服务器配置文件
  • 在调试模式下运行标志(私有区块链)

进展:

  • 从JSON文件读取配置
  • 检查调试模式标志:如果设置了该标志,则不会启动网络同步调度程序,并且不会加载区块链
  • 初始化配置数据结构并启动服务器

服务器


  • 根据协议启动TCP服务器和网络通信。
  • 它有一个提供数据结构,其中包括一个端口号,一个缓冲器大小的,和一个指向types.Settings结构
  • Run ( , handle )
  • handle , protocol.Choice
  • protocol.Choice result . result protocol.Interprete, intrprInterpreteData,
  • 然后,通过intrpr.Commands [0]执行此开关,其中检查以下一项:result,inv,error并且有一个默认
  • 结果部分中,通过intrpr.Commands [1]的值进行了切换,该命令检查缓冲区长度版本的值(在每种情况下都调用相应的函数)

GetVersionBufferLength中的功能都位于srvlib.go文件服务器软件包的。

GetVersion(conn net.Conn, version string)

只是打印到控制台,并将参数中传递的版本发送给客户端:

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

功能

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

如下执行块,事务或其他特定数据的加载:

  • 在控制台上打印需要接受的协议中指定的数据类型:

    fmt.Println("DataType:", intrpr.Commands[2])
  • 读取数值变量buf_len中intrpr.Body
  • 创建一个指定大小缓冲区

    make([]byte, buf_len)
  • 发送好的响应:

    conn.Write([]byte("result:ok"))
  • 从读取流中产生完整的缓冲区:

    io.ReadFull(conn, newbuf)
  • 将缓冲区的内容打印到控制台

    fmt.Println(string(newbuf))

    和读取的字节数

    fmt.Println("Bytes length:", n)
  • 发送好的响应:

    conn.Write([]byte("result:ok"))

服务器包中的方法以这样的方式配置:它们使用协议包中的功能处理接收到的数据

协议


该协议用作在网络通信期间呈现数据的工具。

选择(str字符串)(string,错误)对服务器接收的数据执行初始处理,接收数据的字符串表示形式,并返回为Interprete准备的字符串

  • 使用ReqParseN2(str)将输入字符串分为头和主体
  • head被分解为元素,并使用ReqParseHead(head)放置在命令片中
  • 开关(命令[0])中,我们选择接收到的命令(cmd,键,地址或触发默认部分
  • 在cmd中,检查 2个切换命令(命令[1])-lengthgetversion
  • length检查命令[2]中的数据类型并将其存储在数据类型中
  • 检查正文是否包含字符串值

    len(body) < 1
  • 返回一个响应字符串:

    "result:bufferlength:" + datatype + "/" + body
  • getversion返回一个字符串

    return "result:version/auto"

口译


它包含InterpreteData结构,并对从Choice返回的字符串和InterpreteData对象的形成进行辅助处理

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

功能

Interprete(str string) (*InterpreteData, error)

接受字符串结果并创建对InterpreteData对象的引用

进展:

  • 同样,Choice使用ReqParseN2(str)检索头部和身体
  • 使用ReqParseHead(head)将 head分解为元素
  • InterpreteData对象被初始化,并返回指向它的指针:

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

该对象在主程序包的server.go使用

客户


客户端程序包包含TCPConnectTCPResponseData函数

功能

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

工作方式如下:

  • 与传输的设置对象中指定的连接建立连接

    net.Dial("tcp", s.Host + ":" + s.Port)
  • 在data参数中传递的数据将被传输:

    conn.Write(data)
  • 阅读答案

    resp, n, _ := TCPResponseData(conn, s.BufSize)

    并印在控制台上

    fmt.Println(string(resp[:n]))
  • 如果有效载荷被传输,则其被传输

    conn.Write(payload)

    并读取服务器响应,并将其打印在控制台上

功能

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

创建指定大小的缓冲区,在该处读取服务器响应,然后返回该缓冲区,读取的字节数以及错误对象。

客户例行


用于将命令传输到节点服务器,以及获得简要的统计信息和测试。

它可以采用以下参数:JSON格式的配置文件,要以字符串形式发送到服务器的数据,要发送到有效负载的文件的路径,节点调度程序仿真标志,要以数值形式传输的数据类型。

  • 获取配置

    st := types.ParseConfig(*config)
  • 如果给您一个e标志,则启动sheduler
  • 如果指定了标志f指示文件的路径,则将其数据加载到fdb中并将内容发送到服务器

    client.TCPConnect(st, []byte(CMD_BUFFER_LENGTH + ":" + strconv.Itoa(*t) + "/" + strconv.Itoa(fdblen)), fdb)
  • 如果未指定文件,则仅发送-d标志中的数据

    client.TCPConnect(st, []byte(*data), nil)

所有这些都是显示协议结构的简化视图。在开发过程中,必要的功能已添加到其结构中。

在第二部分中,我将讨论块和事务的数据结构,在第三部分中,将讨论用于从JavaScript连接的WebSocket服务器,在第四部分中,我们将考虑同步调度程序,然后是处理来自输入和输出,密码术和输出池的字节码的堆栈机。

All Articles