pbft的数学证明
在实际的拜占庭容错中,如果n = 3f 1,n个节点的系统可以容忍f个故障节点。
实际拜占庭容错系统中的每个决策都需要2f 1批准,其中fare是故障节点。
我们现在将在数学上证明上述两个定义,它们是彼此的推论。以下计算是斯坦福大学笔记中数学的简化。
分布式系统的两个重要特性是活跃性和安全性。
活跃性
活跃性是系统继续运行时在分布式系统环境中使用的术语。这意味着即使出现一些错误,系统也不会停止运行。在区块链的情况下,活跃意味着系统将继续向链中添加新的区块,并且在任何时候系统都不会停止工作。
安全性
当系统收敛于单个决策时,安全性是分布式系统环境中使用的术语。在分布式系统中,节点可能会分成两个决策或进一步拆分,分布式系统的安全性确保了即使存在故障节点,网络也会在所有可靠节点上以单个决策结束。
证明
非拜占庭式故障
给定网络中的n个节点,具有f个故障节点,保证活跃性和安全性所需的仲裁大小q是多少?
仲裁定义为网络正常运行和做出有效决策所需的最小节点数。仲裁由诚实的节点组成。
活跃度
为了避免网络停止,必须至少存在一个非故障节点。因此,为了活跃度:
q 《= n - f
安全度
为了避免网络分裂成多个决策,大多数决策者应该在线。我们需要一个诚实的多数,仲裁大小应该大于节点总数的一半,以便我们做出有利于我们的决定。
因此,为了安全:
q 》 n/2
2q - n 》 0
通过梳理我们得到的两个条件,
n 《 2q 《=2(n-f)
f 《 n/2
n 》 2f
因此,对于非拜占庭式故障
拜占庭式故障
假设网络中有n个节点,其中f个故障节点可能会遇到拜占庭式故障,那么要保证活跃性和安全性,需要多少仲裁大小q?
遭受拜占庭式失败的节点可以投票给无效的区块或决策,导致决策中的分裂,并因此分叉。
活跃度
为了避免网络停止,必须存在至少一个非故障节点或仲裁。由于拜占庭节点可能无法回复。
因此,为了活跃度:
q 《= n - f
安全性
为了避免网络分裂成多个决策,大多数决策者应该在线。然而,与非拜占庭式失败不同,拜占庭式失败中的节点也可以投票,因此我们需要在投票过程中考虑故障节点。
q 》 n/2
2q - n 》 0
此公式提供了网络中可能存在的最大失败节点数。
因此,为了安全起见,也可以将其写为:
2q - n 》 f
其中f是可容忍故障节点的最大数量。
把这两个条件结合起来
n f 《 2q 《 2(n - f)
n f 《 2n - 2f
3f 《 n
n 》 3f
if n = 3f 1
then 2q 》 4f 1
or
q 》 2f 1/2
因此,对于拜占庭式的失败
qmin = 3f 1
在node.js中实现pbft
在本节中,我们将实现一个以pbft为共识算法的区块链。值得注意的是,该区块链不会使用加密货币,但如果我们使用账户模型,则可以使用。此外,由于pbft易受sybil攻击,因此只能在许可的环境下运行,因此需要了解网络成员。
先决条件
· node.js
· postman
· vs code
架构与设计
为了实施pbft,我们将开发以下组件:
1. 钱包类 - 用于公钥和签名数据。
2. 事务类 - 创建事务并验证它们。
3. 区块类 - 创建区块,验证块并验证区块的提议者。
4. 区块链类 - 创建链,添加区块,计算提议者,验证区块,更新区块。
5. p2p server类 - 在对等体之间广播和接收数据。
6. 验证者 - 生成并验证验证者
7. 事务池,区块池,提交池,准备池和消息池 - 分别存储事务,区块,提交,准备和新轮消息。
8. app - 用于与区块链交互的express api
9. 配置 - 存储全局变量
10. chain utilities - 用于存储散列和验证签名等常用功能。
创建一个根目录pbft并将其cd入其中。此项目中的所有文件都存在于根目录中。
mkdir pbft && cd pbft
chainutil类
我们将从创建一个chain-util.js文件开始,该文件将在此项目中多次使用。此文件将用于创建用于签名数据的密钥对,为事务生成id,散列数据和验证签名。
我们需要三个模块来执行这些功能。因此我们需要安装它们。
npm i --save elliptic uuid crypto-js
创建一个chainutil类并将其导出。
// eddsa allows us to create keypairs
// it is collection of cryptographic algorithms that are used to create keypairs
const eddsa = require(“elliptic”).eddsa;
// ed25519 allows us to create key pair from secret
const eddsa = new eddsa(“ed25519”);
// uuid/v1 creates timestamp dependent ids
const uuidv1 = require(“uuid/v1”);
// used for hashing data to 256 bits string
const sha256 = require(“crypto-js/sha256”);
class chainutil {
// a static function to return keypair generated using a secret phrase
static genkeypair(secret) {
return eddsa.keyfromsecret(secret);
}
// returns ids used in transactions
static id() {
return uuidv1();
}
// hashes any data using sha256
static hash(data) {
return sha256(json.stringify(data)).tostring();
}
// verifies the signed hash by decrypting it with public key
// and matching it with the hash
static verifysignature(publickey, signature, datahash) {
return eddsa.keyfrompublic(publickey).verify(datahash, signature);
}
}
module.exports = chainutil;
pbft_chain_util.js
事务类
接下来,我们将创建一个事务类。在项目文件夹中创建文件transaction.js。此项目中的事务将包含以下属性:
· id - 用于识别
· from - 发件人的地址
· input - 一个进一步包含要存储的数据和时间戳的对象
· hash - 输入的sha256
· signature - 发件人签名的哈希
在文件中创建一个类transaction并将其导出。
// import the chainutil class used for hashing and verification
const chainutil = require(“。/chain-util”);
class transaction {
// the wallet instance will be passed as a parameter to the constructor
// along with the data to be stored.
constructor(data, wallet) {
this.id = chainutil.id();
this.from = wallet.publickey;
this.input = { data: data, timestamp: date.now() };
this.hash = chainutil.hash(this.input);
this.signature = wallet.sign(this.hash);
}
// this method verifies wether the transaction is valid
static verifytransaction(transaction) {
return chainutil.verifysignature(
transaction.from,
transaction.signature,
chainutil.hash(transaction.input)
);
}
}
module.exports = transaction;
pbft-transaction.js
钱包类
接下来是钱包。钱包持有公钥和密钥对。它还负责签署数据哈希并创建签名事务。
在项目目录中创建文件wallet.js。添加一个类wallet并将其导出。
// import the chainutil class used for hashing and verification
const chainutil = require(“。/chain-util”);
// import transaction class used for creating transactions
const transaction = require(“。/transaction”);
class wallet {
// the secret phase is passed an argument when creating a wallet
// the keypair generated for a secret phrase is always the same
constructor(secret) {
this.keypair = chainutil.genkeypair(secret);
this.publickey = this.keypair.getpublic(“hex”);
}
// used for prining the wallet details
tostring() {
return `wallet -
publickey: ${this.publickey.tostring()}`;
}
// used for signing data hashes
sign(datahash) {
return this.keypair.sign(datahash).tohex();
}
// creates and returns transactions
createtransaction(data) {
return new transaction(data, this);
}
// return public key
getpublickey() {
return this.publickey;
}
}
module.exports = wallet;
pbft-wallet.js
验证者类
由于pbft是一种许可的区块链一致性算法,我们需要存储每个节点系统中所有节点的地址。我们可以通过选择一个密钥,创建一个钱包,获取其公钥并将该密钥存储到一个文件中来手动执行此操作。当我们运行我们的项目时,它会读取此文件以获取密钥。
但是,不是手动执行此操作,我们可以通过创建类并添加可以返回n个节点的公钥列表的函数来自动执行此操作。
我们将创建一个validator类,它将生成每个节点都知道的公钥列表。在这个项目中,我们使用了每个节点的密码短语
node1,node2,node3 。..。..
这样,我们就可以更容易地创建公钥列表,并使用相同的公钥从命令行创建节点。
// import the wallet class
const wallet = require(“。/wallet”);
class validators {
// constructor will take an argument which is the number of nodes in the network
constructor(numberofvalidators) {
this.list = this.generateaddresses(numberofvalidators);
}
// this function generates wallets and their public key
// the secret key has been known for demonstration purposes
// secret will be passed from command line to generate the same wallet again
// as a result the same public key will be generatedd
generateaddresses(numberofvalidators) {
let list = [];
for (let i = 0; i 《 numberofvalidators; i ) {
list.push(new wallet(“node” i).getpublickey());
}
return list;
}
// this function verfies if the passed public key is a known validator or not
isvalidvalidator(validator) {
return this.list.includes(validator);
}
}
module.exports = validators;
注意:密钥不应该公开。只有在演示中我们才使用这样的密钥。在实际项目中,发送注册请求以使节点成为验证者。如果整个网络批准此请求,则该节点将成为验证者,并将公钥添加到列表中。
config.js文件
网络中的验证者数量可以通过命令行传递,但更容易将其存储在文件中并在需要的地方导入。
创建一个文件config.js并创建三个变量number_of_nodes,min_approvals和transaction_threshold
// maximum number of transactions that can be present in a block and transaction pool
const transaction_threshold = 5;
// total number of nodes in the network
const number_of_nodes = 3;
// minmu number of positive votes required for the message/block to be valid
const min_approvals = 2 * (number_of_nodes / 3) 1;
module.exports = {
transaction_threshold,
number_of_nodes,
min_approvals
};
pbft-config.js
『本文转载自网络,皇冠最新app版本的版权归原作者所有,如有侵权请联系删除』