文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

python区块链持久化和命令行接口如何实现

2023-06-30 15:37

关注

这篇“python区块链持久化和命令行接口如何实现”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“python区块链持久化和命令行接口如何实现”文章吧。

引言

到目前为止,我们已经构建了一个有工作量证明机制的区块链。有了工作量证明,挖矿也就有了着落。虽然目前距离一个有着完整功能的区块链越来越近了,但是它仍然缺少了一些重要的特性。在今天的内容中,我们会将区块链持久化到一个数据库中,然后会提供一个简单的命令行接口,用来完成一些与区块链的交互操作。本质上,区块链是一个分布式数据库,不过,我们暂时先忽略 “分布式” 这个部分,仅专注于 “存储” 这一点。

选择数据库

目前,我们的区块链实现里面并没有用到数据库,而是在每次运行程序时,简单地将区块链存储在内存中。那么一旦程序退出,所有的内容就都消失了。我们没有办法再次使用这条链,也没有办法与其他人共享,所以我们需要把它存储到磁盘上。

那么,我们要用哪个数据库呢?实际上,任何一个数据库都可以。在 比特币原始论文 中,并没有提到要使用哪一个具体的数据库,它完全取决于开发者如何选择。 Bitcoin Core ,最初由中本聪发布,现在是比特币的一个参考实现,它使用的是 LevelDB。而我们将要使用的是…

couchdb

因为它:

couchdb的安装

docker版couchdb安装,使用docker-compose安装couchdb

# couchdb.yamlversion: '2'services:  couchdb:    image: hyperledger/fabric-couchdb    ports:    - 5984:5984

执行docker-compose -f couchdb.yaml up -d即可安装。
使用http://ip:5984/_utils即可访问couchdb的后台管理系统。

数据库结构

在开始实现持久化的逻辑之前,我们首先需要决定到底要如何在数据库中进行存储。为此,我们可以参考 Bitcoin Core 的做法:

简单来说,Bitcoin Core 使用两个 “bucket” 来存储数据:

此外,出于性能的考虑,Bitcoin Core 将每个区块(block)存储为磁盘上的不同文件。如此一来,就不需要仅仅为了读取一个单一的块而将所有(或者部分)的块都加载到内存中。而我们直接使用couchdb。

在 blocks 中,key -> value 为:

keyvalue
b + 32 字节的 block hashblock index record
f + 4 字节的 file numberfile information record
l + 4 字节的 file numberthe last block file number used
R + 1 字节的 boolean是否正在 reindex
F + 1 字节的 flag name length + flag name string1 byte boolean: various flags that can be on or off
t + 32 字节的 transaction hashtransaction index record

在 chainstate,key -> value 为:

keyvalue
c + 32 字节的 transaction hashunspent transaction output record for that transaction
B32 字节的 block hash: the block hash up to which the database represents the unspent transaction outputs

详情可见 这里。

因为目前还没有交易,所以我们只需要 blocks bucket。另外,正如上面提到的,我们会将整个数据库存储为单个文件,而不是将区块存储在不同的文件中。所以,我们也不会需要文件编号(file number)相关的东西。最终,我们会用到的键值对有:

这就是实现持久化机制所有需要了解的内容了。

序列化

为了方便我们查看,这里我们不直接使用二进制数据,而将其转换为16进制字符串。所以我们需要对区块内容进行序列化。

让我们来实现 Block 的 Serialize 方法:

    # class Block    def serialize(self):        return {            "magic_no": self._magic_no,            "block_header": self._block_header.serialize(),            "transactions": self._transactions        }

直接返回我们需要的数据构成的字典即可,而block_header则需要进一步序列化。它的序列化同样也只需要返回具体的数据字典即可,如下:

    # class BlockHeader    def serialize(self):        return self.__dict__

反序列化则是把信息转换为区块对象。

# class Block    @classmethod    def deserialize(cls, data):        block_header_dict = data['block_header']        block_header = BlockHeader.deserialize(block_header_dict)        transactions = data["transactions"]        return cls(block_header, transactions)

首先反序列化块,然后构造成一个对象,反序列化Header:

# class BlockHeader    @classmethod    def deserialize(cls, data):        timestamp = data.get('timestamp', '')        prev_block_hash = data.get('pre_block_hash', '')        # hash = data.get('hash', '')        hash_merkle_root = data.get('hash_merkle_root', '')        height = data.get('height', '')        nonce = data.get('nonce', '')        block_header = cls(hash_merkle_root, height, prev_block_hash)        block_header.timestamp = timestamp        block_header.nonce = nonce        return block_header

持久化

持久化要做的事情就是把区块数据写入到数据库中,则我们要做的事情有:

创建创世块如下:

# class BlockChain:    def new_genesis_block(self):        if 'l' not in self.db:            genesis_block = Block.new_genesis_block('genesis_block')            genesis_block.set_header_hash()            self.db.create(genesis_block.block_header.hash, genesis_block.serialize())            self.set_last_hash(genesis_block.block_header.hash)

添加一个区块如下:

    def add_block(self, transactions):        """        add a block to block_chain        """        last_block = self.get_last_block()        prev_hash = last_block.get_header_hash()        height = last_block.block_header.height + 1        block_header = BlockHeader('', height, prev_hash)        block = Block(block_header, transactions)        block.mine()        block.set_header_hash()        self.db.create(block.block_header.hash, block.serialize())        last_hash = block.block_header.hash        self.set_last_hash(last_hash)

对couchdb的操作的简单封装如下:

class DB(Singleton):    def __init__(self, db_server_url, db_name='block_chain'):        self._db_server_url = db_server_url        self._server = couchdb.Server(self._db_server_url)        self._db_name = db_name        self._db = None    @property    def db(self):        if not self._db:            try:                self._db = self._server[self._db_name]            except couchdb.ResourceNotFound:                self._db = self._server.create(self._db_name)        return self._db    def create(self, id, data):        self.db[id] = data        return id    def __getattr__(self, name):        return getattr(self.db, name)    def __contains__(self, name):        return self.db.__contains__(name)    def __getitem__(self, key):        return self.db[key]    def __setitem__(self, key, value):        self.db[key] = value

区块链迭代器

由于我们现在使用了数据库存储,不再是数组,那么我们便失去了迭代打印区块链的特性,我们需要重写__getitem__以获得该特性,实现如下:

# class BlockChain(object):    def __getitem__(self, index):        last_block = self.get_last_block()        height = last_block.block_header.height        if index <= height:            return self.get_block_by_height(index)        else:            raise IndexError('Index is out of range')
# class BlockChain(object):    def get_block_by_height(self, height):        """        Get a block by height        """        query = {"selector": {"block_header": {"height": height}}}        docs = self.db.find(query)        block = Block(None, None)        for doc in docs:            block.deserialize(doc)            break        return block

根据区块高度获取对应的区块,此处是利用了couchdb的mongo_query的富查询来实现。

CLI

到目前为止,我们的实现还没有提供一个与程序交互的接口。是时候加上交互了:
这里我们使用argparse来解析参数:

def new_parser():    parser = argparse.ArgumentParser()    sub_parser = parser.add_subparsers(help='commands')    # A print command    print_parser = sub_parser.add_parser(        'print', help='Print all the blocks of the blockchain')    print_parser.add_argument('--print', dest='print', action='store_true')    # A add command    add_parser = sub_parser.add_parser(        'addblock', help='Print all the blocks of the blockchain')    add_parser.add_argument(        '--data', type=str, dest='add_data', help='block data')    return parserdef print_chain(bc):    for block in bc:        print(block)def add_block(bc, data):    bc.add_block(data)    print("Success!")def main():    parser = new_parser()    args = parser.parse_args()    bc = BlockChain()    if hasattr(args, 'print'):        print_chain(bc)    if hasattr(args, 'add_data'):        add_block(bc, args.add_data)if __name__ == "__main__":    main()

测试一下

# 创世块创建$python3 main.pyMining a new blockFound nonce == 19ash_hex == 047f213bcb01f1ffbcdfafad57ffeead0e86924cf439594020da47ff2508291c<Document 'l'@'191-2f44a1493638684d9e000d8dd105192a' {'hash': 'e4f7adac65bcbb304af21be52a1b52bb28c0205a3746d63453d9e8c182de927a'}>Mining a new blockFound nonce == 1ash_hex == 0df1ac18c84a8e524d6fe49cb04aae9af02dd85addc4ab21ac13f9d0d7ffe769<Document 'l'@'192-168ff7ea493ca53c66690985deb5b7ac' {'hash': '01015004e21d394b1a6574eb81896e1c800f18aa22997e96b79bca22f7821a67'}>Block(_block_header=BlockHeader(timestamp='1551317137.2814202', hash_merkle_root='', prev_block_hash='', hash='f20f3c74c831d03aaa2291af23e607896a61809b5ced222483b46795a456a1c5', nonce=None, height=0))Block(_block_header=BlockHeader(timestamp='1551317137.358466', hash_merkle_root='', prev_block_hash='f20f3c74c831d03aaa2291af23e607896a61809b5ced222483b46795a456a1c5', hash='e4f7adac65bcbb304af21be52a1b52bb28c0205a3746d63453d9e8c182de927a', nonce=19, height=1))Block(_block_header=BlockHeader(timestamp='1551317137.4621542', hash_merkle_root='', prev_block_hash='e4f7adac65bcbb304af21be52a1b52bb28c0205a3746d63453d9e8c182de927a', hash='01015004e21d394b1a6574eb81896e1c800f18aa22997e96b79bca22f7821a67', nonce=1, height=2))
$python3 cli.py addblock --data datasMining a new blockFound nonce == 6ash_hex == 0864df4bfbb2fd115eeacfe9ff4d5813754198ba261c469000c29b74a1b391c5<Document 'l'@'193-92e02b894d09dcd64f8284f141775920' {'hash': '462ac519b6050acaa78e1be8c2c8de298b713a2e138d7139fc882f7ae58dcc88'}>Success!

一切正常工作。

以上就是关于“python区块链持久化和命令行接口如何实现”这篇文章的内容,相信大家都有了一定的了解,希望小编分享的内容对大家有帮助,若想了解更多相关的知识内容,请关注编程网行业资讯频道。

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     807人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     351人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     314人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     433人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     221人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯