编写区块链

by Sandeep Panda

通过Sandeep Panda

编写由区块链驱动的在线社区的综合指南 (A comprehensive guide to coding a blockchain-powered online community)

At Hashnode we have been experimenting a lot with blockchain and its use-cases. We have been running a developers’ community ourselves, and the idea behind “decentralized communities” fascinates me a lot. The fact that everyone owns the data and controls the platform can give rise to new types of social apps and disrupt the traditional way of building online communities.

在Hashnode,我们已经在区块链及其用例上进行了大量实验。 我们一直在自己运营一个开发者社区,“分散社区”背后的想法让我着迷。 每个人都拥有数据并控制平台的事实会引发新型的社交应用程序,并破坏建立在线社区的传统方式。

Platforms like Steemit have proven that it’s possible to build such communities and reward users for their contributions. But how should someone go about replicating it and launching their own decentralized social platform powered by blockchain?

像Steemit这样的平台已经证明,可以建立这样的社区并为用户的贡献奖赏。 但是,有人应该如何去复制它并启动自己的由区块链支持的去中心化社交平台?

To answer the question, I took up the challenge of building a decentralized version of HackerNews.

为了回答这个问题,我接受了构建去中心化版本HackerNews的挑战。

During the process, I evaluated multiple platforms and finally zeroed in on a protocol called Tendermint. Using Tendermint, I have built a prototype called “Mint” which can serve as a boilerplate for building blockchain-powered social apps.

在此过程中,我评估了多个平台,最后对名为Tendermint的协议进行了归零 。 我使用Tendermint构建了一个名为“ Mint”的原型,该原型可以用作构建由区块链驱动的社交应用程序的样板。

The codebase is on GitHub. You can check out the following links for code and demo:

该代码库位于GitHub上。 您可以查看以下链接以获得代码和演示:

  • Mint Blockchain

    薄荷区块链

  • Website Code (Blockchain Front-end)

    网站代码(区块链前端)

  • Demo

    演示版

So what does it take to build a blockchain-powered social community where the user-generated data is decentralized? If you are looking for an answer, you have come to the right place. Read on.

那么,要建立一个由区块链驱动的社区,将用户生成的数据分散化,又需要什么呢? 如果您正在寻找答案,那么您来对地方了。 继续阅读。

初步观察 (Preliminary Observations)

Initially, I thought of utilizing an existing platform to build the app. Smart Contract platforms like Ethereum, NEM, NEO, and so on offer storage of assets, but these are not designed to store large amount of data.

最初,我考虑利用现有平台来构建应用程序。 以太坊(Ethereum)NEMNEO等智能合约平台可提供资产存储,但是这些平台并非旨在存储大量数据。

HyperLedger Fabric is compelling, but it’s designed to be deployed in private blockchain networks. Hashgraph sounds interesting, but it’s experimental as of now.

HyperLedger Fabric引人注目,但它旨在部署在私有区块链网络中。 Hashgraph听起来很有趣,但截至目前仍处于试验阶段。

Other potential solutions were: Lisk Sidechains, Loom Network, and BigChainDB. The first two are in private alpha (invite-only), while BigChainDB is powered by Tendermint.

其他可能的解决方案是: Lisk侧链Loom网络BigChainDB 。 前两个位于私有alpha(仅邀请)中,而BigChainDB由Tendermint支持 。

So, instead of using BigChainDB, I decided to play around with Tendermint directly and see what was possible.

因此,我决定不使用BigChainDB,而是直接使用Tendermint,看看有什么可能。

为什么要嫩薄荷 (Why Tendermint)

Tendermint is a protocol that takes care of the consensus layer using BFT algorithm while you just focus on writing the business logic.

Tendermint是一种协议,使用BFT算法可以处理共识层,而您只是专注于编写业务逻辑。

The beauty of the protocol is that you are literally free to choose any programming language to build an interface (Application Blockchain Interface or simply ABCI) that interacts with the blockchain.

该协议的优点在于,您实际上可以自由选择任何编程语言来构建与区块链进行交互的接口(应用程序区块链接口或简称ABCI)。

Tendermint handles the most complex aspects of a blockchain such as block production rounds, peer to peer connectivity, gossiping about new blocks, transaction handling, and more. It stores the transactions on the disk using LevelDB and also delivers the confirmed transaction to your ABCI server so that you can create a global state out of it.

Tendermint处理区块链最复杂的方面,例如区块生产轮次,对等连接,闲聊新区块,交易处理等等。 它使用LevelDB将事务存储在磁盘上,还将确认的事务传递到ABCI服务器,以便您可以从中创建全局状态。

Sounds interesting? Let’s see how to create a blockchain app that stores data on chain using Tendermint.

听起来不错? 让我们看看如何创建一个使用Tendermint在链上存储数据的区块链应用程序。

需要什么 (What’s Needed?)

Here is what you are going to need:

这是您需要的:

  • Macbook / Ubuntu serverMacbook / Ubuntu服务器
  • Golang高朗
  • Tendermint嫩薄荷
  • MongoDBMongoDB
  • And beer… (Coffee lovers can replace this with coffee)还有啤酒……(咖啡爱好者可以用咖啡代替)

设置机器 (Setting up the Machine)

Tendermint is written in Go. So, we need to install the Go language first. Visit this link to check out a few download options. If you are on Ubuntu, you can follow this guide.

Tendermint用Go编写。 因此,我们需要先安装Go语言。 访问此链接以查看一些下载选项。 如果您使用的是Ubuntu,则可以按照本指南进行操作 。

By default, Go chooses $HOME/go as your workspace. If you want to use a different location as your workspace, you can set GOPATH variable in ~/.profile . From now on, we’ll refer to this location as GOPATH.

默认情况下,Go选择$HOME/go作为您的工作区。 如果要使用其他位置作为工作空间,则可以在~/.profile设置GOPATH变量。 从现在开始,我们将这个位置称为GOPATH

Here is how ~/.profile file looks on my machine:

这是~/.profile文件在我的计算机上的外观:

export GOPATH="$HOME/go" export PATH=~/.yarn/bin:$GOPATH/bin:$PATHexport GOBIN="$GOPATH/bin"

Remember to set GOBIN variable as shown above. This is where the Go binaries will be installed.

请记住,如上所示设置GOBIN变量。 这是Go二进制文件的安装位置。

Don’t forget to run source ~/.profile after updating the file.

更新文件后,不要忘记运行源〜/ .profile。

Now we can install Tendermint. Here are the steps:

现在我们可以安装Tendermint。 步骤如下:

  • cd $GOPATH/src/github.com

    cd $GOPATH/src/github.com

  • mkdir tendermint

    mkdir tendermint

  • cd tendermint

    cd tendermint

And finally,

最后,

git clone https://github.com/tendermint/tendermint

This will install the latest version of Tendermint. As I have tested my code against v0.19.7, let’s check out the specific release.

这将安装最新版本的Tendermint。 在针对v0.19.7测试我的代码v0.19.7 ,让我们检查一下特定的发行版。

cd tendermintgit checkout v0.19.7

This will put you on v0.19.7. To proceed with the installation, run the following commands:

这将使您进入v0.19.7。 要继续安装,请运行以下命令:

make get_tools make get_vendor_depsmake install

Congrats! You have installed Tendermint successfully. If everything was installed as intended, the command tendermint version will print out the Tendermint version.

恭喜! 您已经成功安装了Tendermint。 如果一切都按预期安装,则命令tendermint version将打印出Tendermint版本。

Now, you should go ahead and install MongoDB.

现在,您应该继续安装MongoDB 。

编码区块链 (Coding the Blockchain)

If you want to understand how Tendermint works, go through this guide. You may also find the following diagram helpful.

如果您想了解Tendermint的工作原理,请阅读本指南 。 您可能还会发现以下图表很有用。

I’ll outline a few important concepts here:

我将在这里概述一些重要的概念:

  • Tendermint core handles the consensus part.Tendermint核心负责处理共识部分。
  • You need to write an ABCI server that handles the business logic, validations, and so on. Although you can write this in any language, our language of choice will be Go.您需要编写处理业务逻辑,验证等的ABCI服务器。 尽管您可以用任何一种语言编写该语言,但我们选择的语言是Go。
  • Tendermint core will interact with your ABCI server via socket connections.Tendermint核心将通过套接字连接与您的ABCI服务器进行交互。
  • The ABCI server has many methods (JS developers can think of them as callbacks) that will be invoked by Tendermint core on various events.ABCI服务器具有许多方法(JS开发人员可以将它们视为回调),这些方法将由Tendermint核心在各种事件上调用。
  • Two important methods are: CheckTx and DeliverTx. The first one is called to validate a transaction, while the latter is called when the Tx is confirmed.

    两个重要的方法是: CheckTxDeliverTx 。 第一个调用用于验证交易,而第二个则在确认Tx时调用。

  • DeliverTx helps you take necessary actions based on the confirmed transactions. In our case, we’ll use this to create and update our global state stored in MongoDB.

    DeliverTx可帮助您根据已确认的交易采取必要的措施。 在本例中,我们将使用它来创建和更新存储在MongoDB中的全局状态。

  • Tendermint uses BFT consensus. This means more than 2/3 of the validators need to have consensus in order to commit a transaction. So, even if 1/3 of the validators go rogue, the blockchain will still work.Tendermint使用BFT共识。 这意味着超过2/3的验证者需要达成共识才能提交交易。 因此,即使有1/3的验证者流氓,区块链仍然可以工作。
  • In a real world scenario (at least in a public deployment), you will most likely add some sort of consensus such as PoS (Proof of State) in addition to BFT consensus. In this case, we’ll just go ahead with simple BFT consensus. I’ll leave adding PoS up to you.在现实世界中(至少在公共部署中),除了BFT共识之外,您极有可能添加某种共识,例如PoS(状态证明)。 在这种情况下,我们将继续进行简单的BFT共识。 我将继续为您添加PoS。

I suggest that you clone the blockchain ABCI server (code-named mint) from GitHub. But before we go ahead, we need to install a dependency management tool called dep.

我建议您从GitHub克隆区块链ABCI服务器(代号mint )。 但是在继续之前,我们需要安装一个名为dep的依赖项管理工具。

If you are on a Mac, you can just run brew install dep . For Ubuntu, run the following command.

如果您使用的是Mac,则只需运行brew install dep 。 对于Ubuntu,请运行以下命令。

curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh

Now you can clone the codebase of mint.

现在您可以克隆mint的代码库。

cd $GOPATH/srcgit clone https://github.com/Hashnode/mintcd mintdep ensurego install mint

Sweet! You have now installed mint, which is an ABCI server and works along with Tendermint core.

甜! 现在,您已经安装了mint,这是一台ABCI服务器,可以与Tendermint核心一起使用。

Now, let me walk you through the whole set-up and all the code.

现在,让我引导您完成整个设置和所有代码。

入口点 (Entry Point)

You can find the code (and entry point) on GitHub here.

您可以在GitHub上的代码(入口点) 在这里 。

The entry point of the app is mint.go . The most important part of the file is the following section:

该应用程序的入口是mint.go 该文件最重要的部分是以下部分:

app = jsonstore.NewJSONStoreApplication(db)srv, err := server.NewServer("tcp://0.0.0.0:46658", "socket", app) if err != nil {  return err }

All the business logic, methods, and so on are defined in the package jsonstore . The above code simply creates a TCP server on port 46658 that accepts socket connections from Tendermint core.

所有业务逻辑,方法等都在包jsonstore中定义。 上面的代码只是在端口46658上创建一个TCP服务器,该服务器接受来自Tendermint核心的套接字连接。

Now let’s look at jsonstorepackage.

现在让我们看一下jsonstore包。

商业逻辑 (Business Logic)

Here’s the jsonstore repo.

这是 jsonstore

Our ABCI server does two important things:

我们的ABCI服务器执行两项重要的操作:

  • Validates incoming transactions. If a transaction is invalid, it returns an error code and the transaction is rejected.验证传入的事务。 如果交易无效,则返回错误代码,交易被拒绝。
  • Once a transaction is committed (confirmed by > 2/3 of the validators) and stored in LevelDB, the ABCI server updates its global state stored in MongoDB.提交事务(由验证者的> 2/3确认)并将其存储在LevelDB中后,ABCI服务器将更新其存储在MongoDB中的全局状态。

We’re going to use mgo for interacting with MongoDB. So, jsonstore.go defines 5 models that correspond to 5 different MongoDB collections.

我们将使用mgo与MongoDB进行交互。 因此, jsonstore.go定义了5个模型,它们对应于5个不同的MongoDB集合。

The code looks like the following:

该代码如下所示:

// Post ...type Post struct {    ID          bson.ObjectId `bson:"_id" json:"_id"`    Title       string        `bson:"title" json:"title"`    URL         string        `bson:"url" json:"url"`    Text        string        `bson:"text" json:"text"`    Author      bson.ObjectId `bson:"author" json:"author"`    Upvotes     int           `bson:"upvotes" json:"upvotes"`    Date        time.Time     `bson:"date" json:"date"`    Score       float64       `bson:"score" json:"score"`    NumComments int           `bson:"numComments" json:"numComments"`    AskUH       bool          `bson:"askUH" json:"askUH"`    ShowUH      bool          `bson:"showUH" json:"showUH"`    Spam        bool          `bson:"spam" json:"spam"`}
// Comment ...type Comment struct {    ID              bson.ObjectId `bson:"_id" json:"_id"`    Content         string        `bson:"content" json:"content"`    Author          bson.ObjectId `bson:"author" json:"author"`    Upvotes         int           `bson:"upvotes" json:"upvotes"`    Score           float64       `bson:"score" json:"score"`    Date            time.Time    PostID          bson.ObjectId `bson:"postID" json:"postID"`    ParentCommentID bson.ObjectId `bson:"parentCommentId,omitempty" json:"parentCommentId"`}
// User ...type User struct {    ID        bson.ObjectId `bson:"_id" json:"_id"`    Name      string        `bson:"name" json:"name"`    Username  string        `bson:"username" json:"username"`    PublicKey string        `bson:"publicKey" json:"publicKey"`}
// UserPostVote ...type UserPostVote struct {    ID     bson.ObjectId `bson:"_id" json:"_id"`    UserID bson.ObjectId `bson:"userID" json:"userID"`    PostID bson.ObjectId `bson:"postID" json:"postID"`}
// UserCommentVote ...type UserCommentVote struct {    ID        bson.ObjectId `bson:"_id" json:"_id"`    UserID    bson.ObjectId `bson:"userID" json:"userID"`    CommentID bson.ObjectId `bson:"commentID" json:"commentID"`}

We also define a few utility functions such as the following:

我们还定义了一些实用程序功能,例如:

func byteToHex(input []byte) string {    var hexValue string    for _, v := range input {        hexValue += fmt.Sprintf("%02x", v)    }    return hexValue}
func findTotalDocuments(db *mgo.Database) int64 {    collections := [5]string{"posts", "comments", "users", "userpostvotes", "usercommentvotes"}    var sum int64
for _, collection := range collections {        count, _ := db.C(collection).Find(nil).Count()        sum += int64(count)    }
return sum}
func hotScore(votes int, date time.Time) float64 {    gravity := 1.8    hoursAge := float64(date.Unix() * 3600)    return float64(votes-1) / math.Pow(hoursAge+2, gravity)}
// FindTimeFromObjectID ... Convert ObjectID string to Timefunc FindTimeFromObjectID(id string) time.Time {    ts, _ := strconv.ParseInt(id[0:8], 16, 64)    return time.Unix(ts, 0)}

These will be used subsequently in the code.

这些将在代码中随后使用。

内部CheckTx (Inside CheckTx)

Now let’s come to the validation part. How do we accept or reject a transaction? Let’s say someone is trying to sign up, but doesn’t choose a valid username. How can our app validate this?

现在让我们进入验证部分。 我们如何接受或拒绝交易? 假设有人尝试注册,但没有选择有效的用户名。 我们的应用程序如何验证这一点?

It’s done via CheckTx function. The signature looks like the following:

这是通过CheckTx函数完成的。 签名如下所示:

func (app *JSONStoreApplication) CheckTx(tx []byte) types.ResponseCheckTx {
// ... Validation logic}

When a Tendermint node receives a transaction, it invokesCheckTx of ABCI server and passes tx data as a byte array argument. If CheckTx returns a non-zero code, the transaction is rejected.

当Tendermint节点接收到事务时,它将调用CheckTx服务器的CheckTx并将tx数据作为byte数组参数传递。 如果CheckTx返回非零代码,则拒绝该事务。

In our case, clients send Base64 encoded stringified JSON objects to the Tendermint node via an RPC request. So, it is our job to decode the tx and unmarshall the string into a JSON object.

在我们的例子中,客户端通过RPC请求将Base64编码的字符串化JSON对象发送到Tendermint节点。 因此,解码tx并将字符串解组为JSON对象是我们的工作。

It’s done like this:

这样做是这样的:

var temp interface{}err := json.Unmarshal(tx, &temp)if err != nil {  panic(err)}message := temp.(map[string]interface{})

message object typically looks like the following:

message对象通常如下所示:

{  body: {... Message body},  publicKey: <Public Key of Sender>,  signature: <message.body is signed with the Private Key>}

First, we need to make sure that said person has indeed submitted the transaction to the blockchain, not someone else claiming to be that person.

首先,我们需要确保 确实已经提交了交易的blockchain,不是别人自称是那个人。

The best way to validate is to ask clients to sign the message body with the user’s private key and attach both the public key and the signature to the payload. We’ll use ed25519 algorithm to generate the keys and sign the message in the browser and hit the RPC endpoint. In the CheckTx function we’ll again use ed25519 and verify the message with the help of the user’s public key.

验证的最佳方法是要求客户端使用用户的私钥对消息正文进行签名,并将公钥和签名都附加到有效负载上。 我们将使用ed25519算法生成密钥,并在浏览器中对消息签名并点击RPC端点。 在CheckTx函数中,我们将再次使用ed25519并在用户的公共密钥的帮助下验证消息。

It’s done like this:

这样做是这样的:

pubKeyBytes, err := base64.StdEncoding.DecodeString(message["publicKey"].(string))
sigBytes, err := hex.DecodeString(message["signature"].(string))
messageBytes := []byte(message["body"].(string))isCorrect := ed25519.Verify(pubKeyBytes, messageBytes, sigBytes)if isCorrect != true {  return types.ResponseCheckTx{Code: code.CodeTypeBadSignature}}

In the above example, we use the ed25519 package to validate the message. Various codes such as code.CodeTypeBadSignature are defined inside code package. These are just integers. Just remember that if you want to reject a transaction, you have to return a non-zero code. In our case, if we detect that the message signature is not valid, we return CodeTypeBadSignature which is 4.

在上面的示例中,我们使用ed25519包来验证消息。 在code包中定义了各种代码,例如code.CodeTypeBadSignature 。 这些只是整数。 请记住,如果要拒绝交易,则必须返回非零代码。 在我们的例子中,如果我们检测到消息签名无效,则返回CodeTypeBadSignature ,其值为4

The next section of CheckTx deals with various data validations, such as:

CheckTx的下一部分讨论各种数据验证,例如:

  • If the user is sending any transaction other than “createUser (Sign up)”, we first check that the user’s public key is present in our database.如果用户正在发送除“ createUser(Sign up)”以外的任何事务,我们首先检查该用户的公钥是否存在于我们的数据库中。
  • If the user is trying to create a post or comment, it should have valid data such as non-empty title , content , and so on.

    如果用户试图创建帖子或评论,则其应具有有效数据,例如非空titlecontent等。

  • If the user is trying to sign up, the username should have acceptable characters.如果用户尝试注册,则用户名应包含可接受的字符。

The code looks like the following:

该代码如下所示:

// ==== Does the user really exist? ======if body["type"] != "createUser" { publicKey := strings.ToUpper(byteToHex(pubKeyBytes))
count, _ := db.C("users").Find(bson.M{"publicKey": publicKey}).Count()
if count == 0 {  return types.ResponseCheckTx{Code: code.CodeTypeBadData} }}// ==== Does the user really exist? ======
codeType := code.CodeTypeOK
// ===== Data Validation =======switch body["type"] {case "createPost": entity := body["entity"].(map[string]interface{})
if (entity["id"] == nil) || (bson.IsObjectIdHex(entity["id"]. (string)) != true) {  codeType = code.CodeTypeBadData  break }
if entity["title"] == nil || strings.TrimSpace(entity["title"].(string)) == "" {  codeType = code.CodeTypeBadData  break }
if (entity["url"] != nil) && (strings.TrimSpace(entity["url"].(string)) != "") {  _, err := url.ParseRequestURI(entity["url"].(string))  if err != nil {   codeType = code.CodeTypeBadData   break  } }case "createUser": entity := body["entity"].(map[string]interface{})
if (entity["id"] == nil) || (bson.IsObjectIdHex(entity["id"].(string)) != true) {  codeType = code.CodeTypeBadData  break }
r, _ := regexp.Compile("^[A-Za-z_0-9]+$")
if (entity["username"] == nil) || (strings.TrimSpace(entity["username"].(string)) == "") || (r.MatchString(entity["username"].(string)) != true) {  codeType = code.CodeTypeBadData  break }
if (entity["name"] == nil) || (strings.TrimSpace(entity["name"].(string)) == "") {  codeType = code.CodeTypeBadData  break }case "createComment": entity := body["entity"].(map[string]interface{})
if (entity["id"] == nil) || (bson.IsObjectIdHex(entity["id"].(string)) != true) {  codeType = code.CodeTypeBadData  break }
if (entity["postId"] == nil) || (bson.IsObjectIdHex(entity["postId"].(string)) != true) {  codeType = code.CodeTypeBadData  break }
if (entity["content"] == nil) || (strings.TrimSpace(entity["content"].(string)) == "") {  codeType = code.CodeTypeBadData  break }}
// ===== Data Validation =======return types.ResponseCheckTx{Code: codeType}

The code is really simple and pretty self-explanatory. So, I won’t go into the details, and will leave it up to you to read and explore further.

该代码非常简单,而且很容易解释。 因此,我不再赘述,而是由您自己阅读和进一步探讨。

内部DeliverTx (Inside DeliverTx)

Once a transaction is confirmed and applied to the blockchain, Tendermint core calls DeliverTx and passes the transaction as a byte array. The function signature looks like the following:

一旦确认交易并将其应用于区块链,Tendermint核心将调用DeliverTx并将交易作为字节数组传递。 函数签名如下所示:

func (app *JSONStoreApplication) DeliverTx(tx []byte) types.ResponseDeliverTx {  // ... Code goes here}

We’ll use this function to construct a MongoDB-based global state. We do this so that our website users can read the data easily.

我们将使用此函数来构造基于MongoDB的全局状态。 我们这样做是为了使我们的网站用户可以轻松读取数据。

This function is big and has multiple cases. In this section I’ll just cover only one case which is “Post Creation”. As the rest of the code is similar, I’ll leave it up to you to dig deeper and explore the full code.

此功能很大,并且有多种情况。 在本节中,我将仅介绍一种情况,即“后期创建”。 由于其余代码相似,因此我将由您自己进行更深入的研究并探索完整的代码。

Firstly, we’ll go ahead and unmarshall the txdata into a JSON object:

首先,我们将继续将tx数据编组为JSON对象:

var temp interface{}err := json.Unmarshal(tx, &temp)
if err != nil { panic(err)}
message := temp.(map[string]interface{})
var bodyTemp interface{}
errBody := json.Unmarshal([]byte(message["body"].(string)), &bodyTemp)
if errBody != nil { panic(errBody)}
body := bodyTemp.(map[string]interface{})

For post creation, the message object looks like the following:

对于后期创建,消息对象如下所示:

{body: {  type: "createPost",  entity: {    id: id,    title: title,    url: url,    text: text,    author: author  }},signature: signature,publicKey: publicKey}

And here is how DeliverTx function creates a new entry in the database when a “createPost” transaction is committed:

以下是提交“ createPost”事务时, DeliverTx函数如何在数据库中创建新条目:

entity := body["entity"].(map[string]interface{})
var post Postpost.ID = bson.ObjectIdHex(entity["id"].(string))post.Title = entity["title"].(string)
if entity["url"] != nil { post.URL = entity["url"].(string)}if entity["text"] != nil { post.Text = entity["text"].(string)}
if strings.Index(post.Title, "Show UH:") == 0 { post.ShowUH = true} else if strings.Index(post.Title, "Ask UH:") == 0 { post.AskUH = true}
pubKeyBytes, errDecode := base64.StdEncoding.DecodeString(message["publicKey"].(string))
if errDecode != nil { panic(errDecode)}
publicKey := strings.ToUpper(byteToHex(pubKeyBytes))
var user Usererr := db.C("users").Find(bson.M{"publicKey": publicKey}).One(&user)if err != nil { panic(err)}post.Author = user.ID
post.Date = FindTimeFromObjectID(post.ID.Hex())
post.Upvotes = 1
post.NumComments = 0
// Calculate hot rankpost.Score = hotScore(post.Upvotes, post.Date)
// While replaying the transaction, check if it has been marked as spam
spamCount, _ := db.C("spams").Find(bson.M{"postID": post.ID}).Count()
if spamCount > 0 { post.Spam = true}
dbErr := db.C("posts").Insert(post)
if dbErr != nil { panic(dbErr)}
var document UserPostVotedocument.ID = bson.NewObjectId()document.UserID = user.IDdocument.PostID = post.ID
db.C("userpostvotes").Insert(document)

The actual code block has a switch statement that handles each type of transaction differently. Feel free to check out the code and play around. If something is unclear, feel free to write your queries in the comments below.

实际的代码块具有switch语句,该语句以不同的方式处理每种事务。 随时检查代码并尝试一下。 如果不清楚,请随时在下面的评论中写下您的查询。

Now that we’ve examined two important aspects of the ABCI server, let’s try to run both Tendermint core and our server and see how to send transactions.

现在,我们已经检查了ABCI服务器的两个重要方面,让我们尝试同时运行Tendermint核心和我们的服务器,并查看如何发送事务。

In order to run the app, run the following commands from two different terminals.

为了运行该应用程序,请从两个不同的终端运行以下命令。

First, run:

第一次运行:

mint

If the command succeeds, you will see the following output in the terminal:

如果命令成功执行,您将在终端中看到以下输出:

Make sure MongoDB is already running before starting mint. If your terminal is unable to recognize mint command, be sure to run source ~/.profile .

在启动mint之前,请确保MongoDB已经运行。 如果您的终端无法识别mint命令,请确保运行source ~/.profile

Then start Tendermint in a different terminal:

然后在另一个终端中启动Tendermint:

tendermint node --consensus.create_empty_blocks=false

By default, Tendermint produces new blocks every 3 seconds, even if there are no transactions.

默认情况下,即使没有事务,Tendermint也会每3秒生成一次新的块。

To prevent that we use the flag:

为了防止这种情况,我们使用标志:

consensus.create_empty_blocks=false

Now that Tendermint is running you can start sending the transactions to it. You need a client that can generate ed25519 keys, sign your requests, and hit the RPC endpoint exposed by Tendermint.

现在Tendermint正在运行,您可以开始向其发送交易了。 您需要一个可以生成ed25519密钥,对请求进行签名并访问Tendermint公开的RPC端点的客户端。

An example request (Node.js) looks like this:

请求示例(Node.js)如下所示:

const base64Data = req.body.base64Data;
let headers = {    'Content-Type': 'text/plain',    'Accept':'application/json-rpc'}
let options = {    url: "http://localhost:46657",    method: 'POST',    headers: headers,    json: true,    body: {"jsonrpc":"2.0","method":"broadcast_tx_commit","params": { "tx" : base64Data } ,"id":"something"}}
request(options, function (error, response, body) {    res.json({ body: response.body });});

Note that the RPC endpoint is exposed on port 46657 .

请注意,RPC端点在端口4665746657

Forming and signing the requests manually can be tedious. So, I suggest that you use Uphack (a HackerNews style website that interacts with the blockchain) to get the full picture.

手动形成和签署请求可能很乏味。 因此,我建议您使用Uphack (与区块链进行交互的HackerNews风格的网站)来获取完整图片。

To install Uphack, follow the steps below:

要安装Uphack,请按照以下步骤操作:

git clone https://github.com/Hashnode/Uphackcd Uphackyarngulp less // make sure gulp is installed globallynode server.js

You can access the website on http://localhost:3000 . It looks like this on my machine:

您可以访问http://localhost:3000上的网站。 在我的机器上看起来像这样:

As you don’t have any data yet, it will look empty initially. Feel free to register an account and submit some posts to visualize the process.

由于您还没有任何数据,因此最初看起来是空的。 随时注册一个帐户并提交一些帖子以可视化该过程。

While you are using the app, open up the network tab of your browser and check out the XHR section. The /rpc URL accepts the base64 data and makes request to Tendermint’s RPC endpoint server-side. You can copy the base64 data and paste it into a base64 decoder to see the actual data that’s being sent.

使用该应用程序时,打开浏览器的“网络”标签,然后查看“ XHR”部分。 /rpc URL接受base64数据,并向Tendermint的RPC端点服务器端发出请求。 您可以复制base64数据并将其粘贴到base64解码器中,以查看正在发送的实际数据。

Going into the details of Uphack is out of the scope of this tutorial. However, as mentioned above, the code for Uphack (the client) is open source and the logic is straightforward. If you go through the codebase and examine various endpoints, you will develop a better understanding of the whole process.

关于Uphack的详细信息不在本教程的讨论范围之内。 但是,如上所述,Uphack(客户端)的代码是开源的,逻辑很简单。 如果您遍历代码库并检查了各种端点,那么您将对整个过程有更好的了解。

结语 (Wrapping up)

To summarize, we built a blockchain that stores JSON data on chain and accepts transactions in the form of base64. To demonstrate the usage, we also briefly examined Uphack, a HackerNews style website that interacts with the blockchain.

总而言之,我们构建了一个区块链 ,该区块链在链上存储JSON数据并接受base64形式的交易。 为了演示其用法,我们还简要介绍了Uphack,这是一种HackerNews风格的网站 ,可与区块链进行交互。

However, here are a few things you should be aware of:

但是,您应该注意以下几点:

  • You have built a single node network. This means you are the only validator. If you are interested in multi-node deployment, check out mint’s documentation. We have deployed a 4-node network so far, and if you wish to become a validator and play around with the blockchain, feel free to reach out to me.

    您已经建立了一个单节点网络。 这意味着您是唯一的验证者。 如果您对多节点部署感兴趣,请查看mint的文档。 到目前为止,我们已经部署了4节点网络,如果您希望成为验证者并尝试使用区块链,请随时与我联系。

  • This arrangement uses BFT consensus. In a real world scenario you will need some consensus algorithm like Proof of Stake, Delegated Proof of Stake, and so on.此安排使用BFT共识。 在现实世界中,您将需要一些共识算法,例如权益证明,委托权益证明等。
  • The blockchain RPC endpoint listens on 46657 and the ABCI server runs on 46658 . At any time you can check the blockchain status by visiting localhost:46657/status .

    46657链RPC端点在46657侦听,而46657服务器在4665846658 。 您可以随时访问localhost:46657/status来检查localhost:46657/statuslocalhost:46657/status

  • Right now there is no incentive for becoming a validator and producing blocks. In a (D)PoS setting, the block producers should be rewarded with some token every time they propose a block. It’s left as an exercise to you.目前,没有任何动机去成为验证者并生产积木。 在(D)PoS设置中,每当提议区块时,区块生产者应获得一些代币奖励。 它留给您作为练习。
  • The ABCI server can be written in any language. For example, check js-abci.

    ABCI服务器可以用任何语言编写。 例如,检查js-abci 。

To conclude, I would like to make it clear that I am not a blockchain expert. I am a learner and I am just sharing things I find interesting. Storing data on chain fascinates me and I believe decentralized social communities are one of the prime use-cases of blockchain.

总而言之,我想表明我不是区块链专家。 我是一个学习者,我只是分享一些我觉得有趣的东西。 在链上存储数据让我着迷,我相信去中心化社区是区块链的主要用例之一。

If you spot any inaccuracies anywhere in the codebase or article, feel free to point it out. I’ll appreciate if you use mint & Uphack and provide your feedback. PRs are always welcome!

如果您在代码库或文章中的任何地方发现任何不正确之处,请随时指出。 如果您使用mint & Uphack并提供反馈,我们将不胜感激。 随时欢迎公关!

Let me know what you think in the comments below!

让我知道您在以下评论中的想法!

翻译自: https://www.freecodecamp.org/news/a-comprehensive-guide-to-coding-a-blockchain-powered-online-community-f938792dbcb4/

编写区块链

编写区块链_编写由区块链驱动的在线社区的综合指南相关推荐

  1. 程序编写经验教训_编写38本编程书籍的经验教训

    程序编写经验教训 重点 (Top highlight) Unless you've spent a couple of decades coding, you may not remember the ...

  2. 什么是智能合约 区块链_什么是区块链智能合约?

    什么是智能合约 区块链 关于区块链智能合约的第一件事是它们不是智能合约,或者不是区块链上的合约. 实际上,它们的名字是奇异的. 1 ,让我们将按照相反的顺序这些问题,我们应该搞清楚一个聪明的合同实际上 ...

  3. 区块链为什么叫区块链_什么是区块链?

    区块链为什么叫区块链 世界和它的狗为区块链(及相关技术,这是另一篇文章)疯狂了. 在过去的两年中,这项技术大肆宣传过山车,其中包括同样疯狂的估值,技术建议,媒体曝光,监管噩梦,欺诈和未实现的梦想. 但 ...

  4. 以太坊区块链_以太坊区块链搭建与使用(一)-私有链

    步骤 一.下载go语言,并配置环境变量 //以太坊源代码依赖的编译与运行环境 二.通过git clone以太坊源码(go-ethereum),并编译 一.go安装 step1:下载 官方(一般打不开) ...

  5. hb哈勃公链_哈勃公链:打造一站式聚合支付平台

    哈勃智能支付 Hubble Chain (哈勃公链)旨在通过全球先进的底层公链技术.智能合约.稳定的加密资产体系,搭建一个低门槛.安全.便捷的去中心化全球跨境支付系统,致力于为全球DeFi新金融服务带 ...

  6. 初学者用什么编写c语言_编写初学者级教程

    初学者用什么编写c语言 I've been writing books and articles for around ten years. However, like most of us who ...

  7. python编写自定义模块_编写和导入自定义模块/类

    我有一个类,我正试图编写一个名为dbObject的类,并试图从另一个文件夹中的脚本导入它.我的结构如下:/var/www/html/py/testobj.py /var/www/html/py/obj ...

  8. junit编写测试代码_编写数据访问代码测试-不测试框架

    junit编写测试代码 当我们向数据访问代码编写测试时,是否应该测试其公共API的每种方法? 一开始听起来很自然. 毕竟,如果我们不测试所有内容,那么如何知道我们的代码可以按预期工作? 这个问题为我们 ...

  9. 编写junit 测试_编写JUnit测试的另一种方法(Jasmine方法)

    编写junit 测试 最近,我为一个小型个人项目编写了很多Jasmine测试. 我花了一些时间才终于感到正确地完成了测试. 在此之后,当切换回JUnit测试时,我总是很难过. 由于某种原因,JUnit ...

最新文章

  1. oracle往据,指定日期查询数Oracle据库
  2. python import re_Python标准库笔记(2) — re模块
  3. java8默认内存收集器_使用正确的垃圾收集器将Java内存使用量降至最低
  4. Android Fragment手柄后退按钮按下[重复]
  5. java集合之列表:ArrayList、Vector、LinkedList
  6. Java 数据结构之双链表
  7. 2018.06.30 BZOJ1857: [Scoi2010]传送带(三分套三分)
  8. 拼多多:三亿人在用,剩下十亿人在吐槽
  9. IDL——数据的输入、输出与读写
  10. 2019全国知识图谱与语义计算大会
  11. Git操作——廖雪峰Git教程
  12. 8086CPU寻址方式详解
  13. (原创)AD账户误删导致Exchange邮箱被删 莫苦恼
  14. Error: EBUSY: resource busy or locked, lstat ‘D:\pagefile.sys‘
  15. 多重if-else的精简方式
  16. python模拟键盘上键和回车_使用Python模拟键盘输入
  17. 2022哪些蓝牙耳机适合学生党?适合学生党的平价蓝牙耳机推荐
  18. 高达1000美元,扫地机器人Roomba如何成为疫情期间的赢家?
  19. 练习2-6 编写一个函数setbits(x, p ,n, y),该函数返回对x执行下列操作后的结果值: 将x中从第p位开始的n个(二进制)位设置为y中最右边n位的值,x的其余各位保持不变。
  20. 厦大2021级期末上机考试

热门文章

  1. 序列化与反序列化注意事项 java
  2. 浮动元素与兄弟之间的关系 速记 1211
  3. 17-mysql-数据定义语言-ddl
  4. 爬虫-07-请求过程
  5. linux-文件类型-七种
  6. PhpYun人才系统 整合 Ucenter 之后,会员注册提示”该 Email 已经被注册!“的解决方案
  7. Nginx进程间通信机制
  8. 强化顶层设计 巩固网络安全
  9. oracle的redo与undio
  10. 什么是.Net的异步机制(异步Stream读/写) - step 4