用Rust实现区块链 - 6 点对点网络(P2P)
截止到目前,我们在单机上实现了区块链的几乎所有关键特性:随机生成的地址、安全、持久化、工作量证明、UTXO交易。接下来我们将使用rust-libp2p库来实现区块链的p2p网络。
P2P网络
P2P 网络拓扑结构有很多种,有些是中心化拓扑,有些是半中心化拓扑,有些是全分布式拓扑结构。
区块链网络中的全节点就是全分布式拓扑结构,即去中心化的,端到端的网络,节点直接连接到其他节点,它的拓扑结构是扁平的。
SPV节点(简单支付验证节点),随机选择一个全节点进行连接,依赖这个全节点来获取数据,更接近半中心化的拓扑结构。
在这里我们先实现全节点,后面再逐渐完善矿工节点和SPV节点,在本地网络中通过 MDNS 做节点发现,使用 Gossip协议 做消息传播。
数据结构
Node
Node节点的功能包括启动P2P网络节点的消息监听,处理来自命令行的命令消息及其他节点的请求消息。
pub struct Node<T = SledDb> {
bc: Blockchain<T>,
utxos: UTXOSet<T>,
msg_receiver: mpsc::UnboundedReceiver<Messages>,
swarm: Swarm<BlockchainBehaviour>,
}
-
bc:区块链
-
utxos:UTXO集合
-
msg_receiver:通道的接收端,接收其他节点的请求消息。
-
swarm:rust-libp2p的Swarm
BlockchainBehaviour
接收到其他节点的请求消息
#[derive(NetworkBehaviour)]
#[behaviour(event_process = true)]
pub struct BlockchainBehaviour {
pub gossipsub: Gossipsub,
pub mdns: Mdns,
#[behaviour(ignore)]
pub msg_sender: mpsc::UnboundedSender<Messages>,
}
-
gossipsub:使用 Gossip协议 做消息传播
-
mdns:节点发现
-
msg_sender:接收到其他节点的请求消息后,发送到通道中。
命令行消息
#[derive(Debug, Serialize, Deserialize)]
pub enum Commands {
Genesis(String),
Blocks(String),
Sync(String),
CreateWallet(String),
GetAddress(String),
Trans {
from: String,
to: String,
amount: String,
},
}
-
Genesis:创建区块链
-
Blocks:显示区块链信息
-
Sync:同步区块
-
CreateWallet:创建钱包
-
GetAddress:获取地址
-
Trans:创建交易
节点消息
#[derive(Debug, Serialize, Deserialize)]
pub enum Messages {
Version {
best_height: usize,
from_addr: String,
},
Blocks {
blocks: Vec<Block>,
height: usize,
to_addr: String,
},
Block {
block: Block,
}
}
-
Version:向其他节点发送本地节点的区块链高度,同步本地节点。
-
Blocks:向其他节点发送本地区块链信息。
-
Block:向其他节点发送新加入的区块。
消息处理
无论是命令行消息,还是节点消息都采用serde_json进行序列化处理。
创建区块链
Commands::Genesis(addr) => {
if self.bc.get_tip().is_empty() {
self.bc.create_genesis_block(addr.as_str());
self.utxos.reindex(&self.bc)?;
info!("Genesis block was created success!");
}else {
info!("Already exists blockchain, don't need genesis block!");
continue;
}
},
同步区块
处理命令行的同步命令
async fn sync(&mut self) -> Result<()> {
let version = Messages::Version {
best_height: self.bc.get_height(),
from_addr: PEER_ID.to_string(),
};
let line = serde_json::to_vec(&version)?;
self.swarm.behaviour_mut().gossipsub
.publish(BLOCK_TOPIC.clone(), line).unwrap();
Ok(())
}
节点接收到Version消息,如果本地区块链的高度大于其他节点的高度,则向其发送区块链信息。
async fn process_version_msg(&mut self, best_height: usize, from_addr: String) -> Result<()> {
if self.bc.get_height() > best_height {
let blocks = Messages::Blocks {
blocks: self.bc.get_blocks(),
height: self.bc.get_height(),
to_addr: from_addr,
};
let msg = serde_json::to_vec(&blocks)?;
self.swarm.behaviour_mut().gossipsub
.publish(BLOCK_TOPIC.clone(), msg).unwrap();
}
Ok(())
}
节点接收到区块链信息后,同步到本地节点。
async fn process_blocks_msg(&mut self, blocks: Vec<Block>, to_addr: String, height: usize) -> Result<()> {
if PEER_ID.to_string() == to_addr && self.bc.get_height() < height {
for block in blocks {
self.bc.add_block(block)?;
}
self.utxos.reindex(&self.bc).unwrap();
}
Ok(())
}
创建交易、挖矿
由于是全节点,为了简便,在这里交易的创建与挖矿放在一起处理了。
async fn mine_block(&mut self, from: &str, to: &str, amount: i32) -> Result<()> {
let tx = Transaction::new_utxo(from, to, amount, &self.utxos, &self.bc);
let txs = vec![tx];
let block = self.bc.mine_block(&txs);
self.utxos.reindex(&self.bc).unwrap();
let b = Messages::Block { block };
let line = serde_json::to_vec(&b)?;
self.swarm.behaviour_mut().gossipsub
.publish(BLOCK_TOPIC.clone(), line).unwrap();
Ok(())
}
验证
启动第一个节点
RUST_LOG=info cargo run --quiet server data
Local peer id: PeerId("12D3KooWHn6sTgQU7bwKfPQHXi2oo4dDUEneoVFWtfur7bufXuZ7")
Listening on "/ip4/127.0.0.1/tcp/53664"
执行命令后,可以看到节点已经启动,生成了唯一的节点ID,在本地53664端口监听消息。
1,查看区块链信息
{"Blocks":""}
INFO blockchain_rust_part_6::networks::node: tip:
INFO blockchain_rust_part_6::networks::node: height: 0
可以看到这个节点上还没有区块链。
2,创建一个用户的钱包地址
{"CreateWallet":"justin"}
INFO blockchain_rust_part_6::networks::node: justin's address is 1KooomKwhgPCfB2YfnKT7yMUxGcVWqS3ns
地址已经创建,我们把这个地址记录下来。
3,下面执行创建区块链命令
{"Genesis":"1KooomKwhgPCfB2YfnKT7yMUxGcVWqS3ns"}
INFO blockchain_rust_part_6::networks::node: Genesis block was created success!
区块链创建成功。
再次查看
{"Blocks":""}
INFO blockchain_rust_part_6::blocks::blockchain: Block {
header: BlockHeader {
......
},
tranxs: [
Transaction {
id: "24cde4a1782d23fda6ec353959f9197450078a1d79126c82e83898d92015ec96",
vin: [
Txinput {
txid: "",
vout: 0,
signature: [],
pub_key: [],
},
],
vout: [
Txoutput {
value: 10,
pub_key_hash: [
......
],
},
],
},
],
hash: "00ca23fdb684b0ebf29eb344e2f9c1ee2ba8325aceba8a7474dbcb77549a2bc9",
}
INFO blockchain_rust_part_6::networks::node: tip: 00ca23fdb684b0ebf29eb344e2f9c1ee2ba8325aceba8a7474dbcb77549a2bc9
INFO blockchain_rust_part_6::networks::node: height: 1
启动第二个节点
RUST_LOG=info cargo run --quiet server data1
PeerId("12D3KooWDknz5ScSw8Ye2ULheq3DHUexhRkH9z1y7N7a27XyWphs")
Listening on "/ip4/127.0.0.1/tcp/53891"
第二个节点节点启动成功,生成了唯一的节点ID,在本地53891端口监听消息。
1,查看区块链信息
{"Blocks":""}
INFO blockchain_rust_part_6::networks::node: tip:
INFO blockchain_rust_part_6::networks::node: height: 0
在第二个节点上还没有区块链。
2,同步区块链
{"Sync":""}
INFO blockchain_rust_part_6::networks::behaviour: Got message with id: 37343836383930393039373131393933343631 from peer: PeerId("12D3KooWHn6sTgQU7bwKfPQHXi2oo4dDUEneoVFWtfur7bufXuZ7")
从第一个节点:12D3KooWHn6sTgQU7bwKfPQHXi2oo4dDUEneoVFWtfur7bufXuZ7,同步区块链成功。
再次查看
{"Blocks":""}
Apr 19 14:00:53.136 INFO blockchain_rust_part_6::blocks::blockchain: Block {
header: BlockHeader {
......
},
tranxs: [
Transaction {
id: "24cde4a1782d23fda6ec353959f9197450078a1d79126c82e83898d92015ec96",
vin: [
Txinput {
txid: "",
vout: 0,
signature: [],
pub_key: [],
},
],
vout: [
Txoutput {
value: 10,
pub_key_hash: [
......
],
},
],
},
],
hash: "00ca23fdb684b0ebf29eb344e2f9c1ee2ba8325aceba8a7474dbcb77549a2bc9",
}
Apr 19 14:00:53.136 INFO blockchain_rust_part_6::networks::node: tip: 00ca23fdb684b0ebf29eb344e2f9c1ee2ba8325aceba8a7474dbcb77549a2bc9
Apr 19 14:00:53.137 INFO blockchain_rust_part_6::networks::node: height: 1
与第一个节点的区块链一致。
启动第三个节点,步骤同第二个节点
创建交易
1,在第二个节点创建一个用户的钱包地址
{"CreateWallet":"Bob"}
INFO blockchain_rust_part_6::networks::node: Bob's address is 1EuM1UEhJFTDR5UfWzfghzv82bCdwRWk9E
2,在第一个节点创建交易
由Justin向Bob发送4单位货币
{"Trans": {"from":"1KooomKwhgPCfB2YfnKT7yMUxGcVWqS3ns","to":"1EuM1UEhJFTDR5UfWzfghzv82bCdwRWk9E","amount":"4"}}
3,在所有节点查看区块链信息
{"Blocks":""}
Apr 19 14:11:38.410 INFO blockchain_rust_part_6::blocks::blockchain: Block {
header: BlockHeader {
......
},
tranxs: [
Transaction {
id: "1578eda9b3ba5f5be584ddb65389ac5172befa1ba50cf03a90fcdafdb5ce4bea",
vin: [
Txinput {
txid: "24cde4a1782d23fda6ec353959f9197450078a1d79126c82e83898d92015ec96",
vout: 0,
signature: [
......
],
pub_key: [
......
],
},
],
vout: [
Txoutput {
value: 4,
pub_key_hash: [
......
],
},
Txoutput {
value: 6,
pub_key_hash: [
......
],
},
],
},
],
hash: "0056d99918490fd8d650d247722234c1d17f18d9073a39d2eacb16550d9737df",
}
Apr 19 14:11:38.411 INFO blockchain_rust_part_6::blocks::blockchain: Block {
header: BlockHeader {
......
},
tranxs: [
Transaction {
id: "24cde4a1782d23fda6ec353959f9197450078a1d79126c82e83898d92015ec96",
vin: [
Txinput {
txid: "",
vout: 0,
signature: [],
pub_key: [],
},
],
vout: [
Txoutput {
value: 10,
pub_key_hash: [
......
],
},
],
},
],
hash: "00ca23fdb684b0ebf29eb344e2f9c1ee2ba8325aceba8a7474dbcb77549a2bc9",
}
INFO blockchain_rust_part_6::networks::node: tip: 0056d99918490fd8d650d247722234c1d17f18d9073a39d2eacb16550d9737df
INFO blockchain_rust_part_6::networks::node: height: 2
所有节点都已经同步了区块信息。
工程结构
完整代码:
https://github.com/Justin02180218/blockchain_rust
更多文章,请关注公众号:coding到灯火阑珊