亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

python區塊鏈持久化和命令行接口如何實現

發布時間:2022-05-25 09:19:49 來源:億速云 閱讀:114 作者:iii 欄目:開發技術

這篇“python區塊鏈持久化和命令行接口如何實現”文章的知識點大部分人都不太理解,所以小編給大家總結了以下內容,內容詳細,步驟清晰,具有一定的借鑒價值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來看看這篇“python區塊鏈持久化和命令行接口如何實現”文章吧。

    引言

    到目前為止,我們已經構建了一個有工作量證明機制的區塊鏈。有了工作量證明,挖礦也就有了著落。雖然目前距離一個有著完整功能的區塊鏈越來越近了,但是它仍然缺少了一些重要的特性。在今天的內容中,我們會將區塊鏈持久化到一個數據庫中,然后會提供一個簡單的命令行接口,用來完成一些與區塊鏈的交互操作。本質上,區塊鏈是一個分布式數據庫,不過,我們暫時先忽略 “分布式” 這個部分,僅專注于 “存儲” 這一點。

    選擇數據庫

    目前,我們的區塊鏈實現里面并沒有用到數據庫,而是在每次運行程序時,簡單地將區塊鏈存儲在內存中。那么一旦程序退出,所有的內容就都消失了。我們沒有辦法再次使用這條鏈,也沒有辦法與其他人共享,所以我們需要把它存儲到磁盤上。

    那么,我們要用哪個數據庫呢?實際上,任何一個數據庫都可以。在 比特幣原始論文 中,并沒有提到要使用哪一個具體的數據庫,它完全取決于開發者如何選擇。 Bitcoin Core ,最初由中本聰發布,現在是比特幣的一個參考實現,它使用的是 LevelDB。而我們將要使用的是…

    couchdb

    因為它:

    • 簡單易用

    • 有一個web的UI界面,方便我們查看

    • 豐富的查詢支持

    • 良好的python支持

    couchdb的安裝

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

    # couchdb.yaml
    version: '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” 來存儲數據:

    • 其中一個 bucket 是 blocks,它存儲了描述一條鏈中所有塊的元數據

    • 另一個 bucket 是 chainstate,存儲了一條鏈的狀態,也就是當前所有的未花費的交易輸出,和一些元數據

    此外,出于性能的考慮,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)相關的東西。最終,我們會用到的鍵值對有:

    • 32 字節的 block-hash(轉換為16進制字符串) -> block 結構

    • l -> 鏈中最后一個塊的 hash(轉換為16進制字符串)

    這就是實現持久化機制所有需要了解的內容了。

    序列化

    為了方便我們查看,這里我們不直接使用二進制數據,而將其轉換為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

    持久化

    持久化要做的事情就是把區塊數據寫入到數據庫中,則我們要做的事情有:

    • 檢查數據庫是否已經有了一個區塊鏈

    • 如果沒有則創建一個,創建創世塊并將l指向這個塊的哈希

    • 添加一個區塊,將l指向新添加的區塊哈希

    創建創世塊如下:

    # 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 parser
    def 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.py
    Mining a new block
    Found nonce == 19ash_hex == 047f213bcb01f1ffbcdfafad57ffeead0e86924cf439594020da47ff2508291c
    <Document 'l'@'191-2f44a1493638684d9e000d8dd105192a' {'hash': 'e4f7adac65bcbb304af21be52a1b52bb28c0205a3746d63453d9e8c182de927a'}>
    Mining a new block
    Found 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 datas
    Mining a new block
    Found nonce == 6ash_hex == 0864df4bfbb2fd115eeacfe9ff4d5813754198ba261c469000c29b74a1b391c5
    <Document 'l'@'193-92e02b894d09dcd64f8284f141775920' {'hash': '462ac519b6050acaa78e1be8c2c8de298b713a2e138d7139fc882f7ae58dcc88'}>
    Success!

    一切正常工作。

    以上就是關于“python區塊鏈持久化和命令行接口如何實現”這篇文章的內容,相信大家都有了一定的了解,希望小編分享的內容對大家有幫助,若想了解更多相關的知識內容,請關注億速云行業資訊頻道。

    向AI問一下細節

    免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

    AI

    合肥市| 凤山县| 绥江县| 遵义县| 宝清县| 靖边县| 阿尔山市| 凤阳县| 阜新市| 嵊州市| 阿坝| 新宁县| 宁安市| 南丹县| 镶黄旗| 平安县| 五华县| 峨边| 宁国市| 黄大仙区| 涡阳县| 东平县| 册亨县| 高陵县| 西丰县| 巴彦淖尔市| 云阳县| 金川县| 平利县| 盐源县| 梁山县| 石城县| 桐乡市| 浙江省| 镶黄旗| 白水县| 兴义市| 桦川县| 司法| 武冈市| 甘南县|