The translation of the series of articles I have put on the GitHub: blockchain-tutorial, follow-up updates will be on the GitHub, may not be synchronized here. If you want to run the code directly, you can also clone GitHub on the Tutorial warehouse and go to the SRC directory to execute make. Introduction
So far, we've built prototypes that have all the key features of the block chain: Anonymous, secure, randomly generated addresses, block-chain data storage, work-proof systems, and reliably store transactions. Although these features are essential, there are still deficiencies. The ability to make these properties truly glow, making it possible to encrypt money, is the Network (network). If the implementation of such a block chain is only running on a single node, what is the use of it. If there is only one user, then what is the use of these cryptography-based features? It is because of the network that the whole mechanism can operate and glow and heat.
You can think of these block-chain characteristics as rules, similar to the rules of human life, the establishment of breeding, a social arrangement. A block chain network is a program community in which every program follows the same rules, and it is because of the same rule that the network can last forever. Similarly, when people have the same idea, they can hold their fists together and build a better life. If people follow different rules, they will live in a divided community (state, commune, etc.). Similarly, if a block chain node follows different rules, a split network will also be formed.
The point is : if there is no network, or most of the nodes do not follow the same rules, then the rules will be useless, useless. Disclaimer: Unfortunately, I don't have enough time to realize a true Peer-to-peer network prototype. In this article I'll show you one of the most common scenarios that involve different types of nodes. It is a good challenge and practice to continue to improve this scenario and realize it as a peer-to-peer network. In addition to the scenario in this article, I cannot guarantee that the other scenarios will work properly. I'm sorry.
The code implementation of this article has changed a lot, please click here to see all the code changes. Block Chain Network
Block chain networks are centralized, which means that without servers, clients do not need to rely on servers to get or process data. In the block chain network, there are some nodes, each node is a complete (full-fledged) member of the network. The node is everything: It is both a client and a server. This needs to be kept in mind because it is very different from the traditional Web application.
A block chain network is a peer-to-peer (Peer-to-peer, end-to-end) network in which nodes are connected directly to other nodes. Its topology is flat because there is no hierarchy in the node's world. Here's a schematic of it:
Business Vector created by dooder-freepik.com
It is more difficult to implement such a network node because they have to do a lot of things. Each node must interact with many other nodes, it must request the state of the other node, compare its own state, and update when the state is obsolete. node Role
Although nodes have fully mature properties, they can also play different roles in the network. For example: Miners
Such nodes run on powerful or proprietary hardware (such as ASIC), and their only goal is to dig up new blocks as quickly as possible. Miners are the only role in the block chain that can be used to prove the workload, because digging actually means solving PoW problems. In the block chain of the equity certificate PoS, there is no mining. Full node
These nodes verify the effectiveness of the blocks dug up by the miners and confirm the transaction. To do this, they must have a full copy of the block chain. At the same time, the whole node performs routing operations to help other nodes discover each other. For the network, a very important section is to have enough of the whole node. Because it is these nodes that perform the decision-making function: they determine the validity of a block or a transaction. SPV
SPV represents simplified Payment verification, simple payment verification. These nodes do not store copies of the entire block chain, but can still validate transactions (but not all transactions, but a subset of transactions, such as transactions sent to a given address). A SPV node relies on a whole node to obtain data, and there may be multiple SPV nodes connected to a whole node. The SPV makes wallet applications possible: a person does not need to download the entire block chain, but can still verify his transaction. Network Simplification
In order to implement the network in the current block-chain prototype, we have to simplify something. Because we don't have a lot of computers to simulate a multi-node network. Of course, we can use virtual machines or Docker to solve this problem, but it makes everything more complicated: you will have to solve the virtual machines or docker problems that may arise, and my goal is to put all my energy into the block chain implementation. So, we want to run multiple block chain nodes on a single machine and expect them to have different addresses. To do this, we will use the port number as the node identifier instead of using an IP address, such as a node that will have such an address:127.0.0.1:3000,127.0.0.1:3001, 127.0.0.1:3002 and so on. We call it the Port node ID and set it using the environment variable node_id. Therefore, you can open multiple terminal windows and set different node_id to run different nodes.
This method also needs to have different block chains and wallet files. They must now be named after the node ID, such as Blockchain_3000.db, Blockchain_30001.db and Wallet_3000.db, wallet_30001.db, and so on. Implement
So, what happens when you download Bitcoin Core and run it for the first time? It must be connected to a node to download the latest state of the block chain. Considering that your computer is unaware of all or part of the Bitcoin node, what is connected to "a node".
Hard-coded an address in Bitcoin Core has been proven to be an error: Because nodes can be attacked or shut down, this can cause new nodes to be unable to join the network. In Bitcoin Core, the DNS seeds is hard-coded. Although these are not nodes, the DNS server knows the addresses of some nodes. When you start a new Bitcoin Core, it connects to a seed node, gets a full list of nodes, and then downloads the chunk chain from those nodes.
However, in our current implementation, can not be fully centralized, because there will be the characteristics of the central. We will have three nodes: a central node. All other nodes connect to this node, which sends data between the other nodes. A miner node. This node will store a new transaction in the memory pool, and when there are enough transactions, it will be packaged and dug up a new block. A wallet node. This node will be used to send coins between wallets. However, unlike the SPV node, it stores a complete copy of the block chain. Scene
The goal of this article is to achieve the following scenario: The central node creates a block chain. One other (wallet) node connects to the central node and downloads the block chain. Another (miner) node connects to the central node and downloads the block chain. The wallet node creates a transaction. The miner node receives the transaction and saves the transaction to the memory pool. When there is enough trading in the memory pool, the miners start digging a new block. When a new block is dug out, it is sent to the central node. The wallet node synchronizes with the hub node. Users of the Wallet node check that their payment is successful.
This is the general process in the Bitcoin. Although we will not achieve a real peer-to-peer network, but we will achieve a true, is also the most important bit of Bitcoin user scenarios. version
Nodes communicate through messages (message). When a new node starts running, it takes several nodes from a DNS seed and sends them a version message, which looks like this in our implementation:
Type version struct {
version int
bestheight int
addrfrom string
}
Since we have only one block chain version, the version field does not actually store any important information. Bestheight the height of the nodes in the storage block chain. Addfrom stores the sender's address.
What should the node that received the version message do? It responds to its own version message. This is a handshake?: There can be no other communication without greeting each other beforehand. However, this is not polite: version is used to find a longer block chain. When a node receives the version message, it checks whether the block chain of this node is larger than the Bestheight value. If not, the node requests and downloads the missing blocks.
To receive messages, we need a server:
var nodeaddress string
var knownnodes = []string{"localhost:3000"}
func StartServer (NodeID, mineraddress String) {
nodeaddress = fmt. Sprintf ("localhost:%s", NodeID)
miningaddress = mineraddress
ln, err: = Net. Listen (Protocol, nodeaddress)
defer ln. Close ()
BC: = Newblockchain (NodeID)
if nodeaddress!= knownnodes[0] {
sendversion (knownnodes[0], BC)
for {conn, err: = ln. Accept () Go
handleconnection (conn, BC)
}
}
First, we hard-code the address of the hub node: Because each node must know where to start initializing. The mineraddress parameter specifies the address to receive the mining award. Code fragment:
If Nodeaddress!= knownnodes[0] {
sendversion (knownnodes[0], BC)
}
This means that if the current node is not a central node, it must send a version message to the hub node to query whether its own chunk chain is obsolete.
Func sendversion (addr string, BC *blockchain) {
bestheight: = BC. Getbestheight ()
Payload: = Gobencode (Version{nodeversion, Bestheight, nodeaddress})
Request: = Append ( Commandtobytes ("version"), payload ...)
SendData (addr, request)
}
Our message, at the bottom, is the byte sequence. The first 12 bytes specify the command name (such as version here), and the following bytes contain the GOB encoded message structure, Commandtobytes looks like this:
Func commandtobytes (command string) []byte {
var bytes [Commandlength]byte
for I, c: = Range command {
bytes[i ] = Byte (c)
} return
bytes[:]
}
It creates a 12-byte buffer, fills it with a command name, and leaves the remaining bytes blank. The following is an inverse function:
Func bytestocommand (bytes []byte) string {
var command []byte
for _, B: = Range bytes {
if B!= 0x0 {
comm and = append (command, b)
}
} return
FMT. Sprintf ("%s", command)
}
When a node receives a command, it runs bytestocommand to extract the command name and select the correct processor to process the command body:
Func handleconnection (Conn net. Conn, BC *blockchain) {
request, err: = Ioutil. ReadAll (conn)
command: = Bytestocommand (Request[:commandlength])
FMT. Printf ("Received%s command\n", command)
switch command {
...
Case "Version":
handleversion (Request, BC)
default:
FMT. Println ("Unknown command!")
}
Conn. Close ()
}
The following is the version command processor:
Func handleversion (Request []byte, BC *blockchain) {
var buff bytes. Buffer
var payload verzion
Buff. Write (request[commandlength:])
dec: = gob. Newdecoder (&buff)
err: = Dec. Decode (&payload)
mybestheight: = BC. Getbestheight ()
foreignerbestheight: = payload. Bestheight
If Mybestheight < foreignerbestheight {
sendgetblocks (payload. Addrfrom)
} else if mybestheight > foreignerbestheight {
sendversion (payload. Addrfrom, BC)
}
if!nodeisknown (payload. Addrfrom) {
knownnodes = append (knownnodes, payload. Addrfrom)
}
}
First, we need to decode the request and extract the valid information. All of the processors are similar in this section, so we'll omit this section from the code snippet below.
The
Then the node will compare the BestHeight from the message to itself. If the block chain of its own node is longer, it will reply to the version message, otherwise it will send a getblocks message. Getblocks