溫馨提示×

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

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Plasma Cash 合約解讀

發(fā)布時(shí)間:2020-07-31 18:35:42 來源:網(wǎng)絡(luò) 閱讀:455 作者:蟲洞社區(qū) 欄目:開發(fā)技術(shù)

作者介紹

蟲洞社區(qū)·簽約作者 steven bai

Plasma Cash 合約解讀

  • Plasma Cash 合約解讀
    • 1. 合約代碼
    • 2. 合約文件簡單介紹
    • 3. Plasma Cash 的基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)
      • 3.1 Plasma Cash 中的資產(chǎn)
      • 3.2 Plasma Cash中的交易
      • 3.3 Plasma Cash 中的 Block
      • 3.4 Plasma Cash 中資產(chǎn)的回歸主鏈以太坊
        • 3.4.1 資產(chǎn)擁有證明
        • 3.4.2 等待其他人來挑戰(zhàn)我
        • 3.4.3 挑戰(zhàn)期過了, Bob 拿回資產(chǎn) uid
    • 4. Plasma Cash 中的退出示例
    • 5. 其他問題

此文來自 SmartMesh 團(tuán)隊(duì),轉(zhuǎn)載請(qǐng)聯(lián)系作者。

Plasma 由 V 神在2017年8月提出,希望通過鏈下交易來大幅提高以太坊的 TPS.

每條 Plasma 鏈都會(huì)將有關(guān)交易順序的消息換算成一個(gè)哈希值存儲(chǔ)在根鏈上。比特幣和以太坊都屬于根鏈——這兩條區(qū)塊鏈具有很高的安全性,并且通過去中心化保證了(安全性和活性)。

Plasma 設(shè)計(jì)模型有兩個(gè)主要的分支:Plasma MVP 和 Plasma Cash 。這里我們來研究 SmartPlasma 實(shí)現(xiàn)的 Plasma Cash 合約,并通過合約分析來回答大家關(guān)于 Plasma Cash 的一系列疑問.

1. 合約代碼

SmartPlasma的合約代碼肯定會(huì)不斷升級(jí),我針對(duì)他們?cè)诮裉?2018-09-14)最新版本進(jìn)行分析,這份代碼目前保存在我的 github 上 plasma cash.

2. 合約文件簡單介紹

文件夾中有不少與 Plasma Cash 無關(guān)的合約,這里只關(guān)注直接與 Plasma Cash 相關(guān)合約,像 ERC20Token 相關(guān)合約就忽略,自行查看.

  • Mediator.sol 是 Plasma Cash 鏈中資產(chǎn)的進(jìn)出口
  • RootChain.sol 處理 Plasma Cash 子鏈(相對(duì)于以太坊而言)中的交易以及打包等
  • libraries/MerkleProof.sol 是子鏈中交易用到的默克爾樹,用于子鏈參與方進(jìn)行欺詐證明 fraud proof.
  • libraris/RLP.sol RLP編碼支持,可以暫時(shí)忽略,子鏈中所有的交易都是用RLP 編碼的.
  • libraries/PlasmaLib.sol 生成 uid 的輔助函數(shù)
  • ECRecovery.sol 簽名驗(yàn)證
  • datastructures/Transaction.sol 描述交易的數(shù)據(jù)結(jié)構(gòu)

3. Plasma Cash 的基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)

Plasma Cash 是一種子鏈結(jié)構(gòu),可以認(rèn)為 Plasma Cash 是以太坊的一個(gè)是基于 =一種簡化的UTXO模型的子鏈.

3.1 Plasma Cash 中的資產(chǎn)

Plasma Cash 中的資產(chǎn)都來自于以太坊,但是一旦進(jìn)入 Plasma Cash 就會(huì)擁有唯一的 ID,并且不可分割.
可以參考 Mediator.sol的deposit函數(shù). Mediator就是 Plasma Cash 資產(chǎn)存放的地方.

        /** @dev Adds deposits on Smart Plasma.
     *  @param currency Currency address.
     *  @param amount Amount amount of currency.
     */
    function deposit(address currency, uint amount) public {
        require(amount > 0);

        Token token = Token(currency);
        token.transferFrom(msg.sender, this, amount); /// deposit test1

        bytes32 uid = rootChain.deposit(msg.sender, currency, amount); /// deposit test2
        cash[uid] = entry({
            currency: currency,
            amount: amount
        });
    }

通過合約可以看出進(jìn)入 Plasma Cash 的資產(chǎn)必須是 ERC20 Token,這些資產(chǎn)實(shí)際上是存在 Mediator 這個(gè)合約上,然后由 RootChain 為其分配一個(gè)唯一的 ID, 也就是 uid. 這個(gè) uid 代表著什么 token, 有多少個(gè).

3.2 Plasma Cash中的交易

關(guān)鍵代碼在 Transaction.sol中.

    struct Tx {
        uint prevBlock;
        uint uid;
        uint amount;
        address newOwner;
        uint nonce;
        address signer;
        bytes32 hash;
    }

這里可能不太明顯,需要解釋才能看出來這是一個(gè) UTXO 交易的模型. 這里面的amount 和 hash 實(shí)際上都有點(diǎn)啰唆,可以忽略. 那么剩下的成員需要來解釋.

prevBlock就是 UTXO 中的輸入,來自于哪塊. 至于為什么沒有像比特幣一樣的OutPoint 結(jié)構(gòu),也就是 TxHash+Index, 后續(xù)會(huì)講到.
uid 就是交易的資產(chǎn) ID
newOwner 交易輸出給誰, 這里也不支持像 比特幣一樣的腳本.
nonce 是這筆資產(chǎn)的第多少次交易,在雙花證明中有重要作用.
signer必須由資產(chǎn)原擁有者的簽名.

amount 不重要,是因?yàn)橘Y產(chǎn)不可分割,導(dǎo)致這里的 Amount 不會(huì)隨交易發(fā)生而發(fā)生變化. 而 hash 則是可以直接計(jì)算出來.

3.3 Plasma Cash 中的 Block

如果一般區(qū)塊鏈中的 Block 一樣,他是交易的集合.但是不同于一般鏈的是,這里面的礦工(不一定是 Operator)不僅需要維護(hù)好子鏈,還需要周期性的將每一個(gè) Block 對(duì)應(yīng)的默克爾樹根保存到以太坊中,這個(gè)工作只能有 Operator 來完成.
具體代碼可見 RootChain.sol的.

    function newBlock(bytes32 hash) public onlyOperator {
        blockNumber = blockNumber.add(uint256(1));
        childChain[blockNumber] = hash;

        NewBlock(hash);
    }

交易證據(jù)提交者只能是 Operator, 也就是合約的創(chuàng)建者. 這個(gè) Operator 既可以是普通賬戶,這時(shí)他就是這個(gè)子鏈的管理員.也可以是一份合約,那么就可以通過合約來規(guī)定子鏈的出塊規(guī)則.

3.4 Plasma Cash 中資產(chǎn)的回歸主鏈以太坊

當(dāng)資產(chǎn)在 Plasma 中交易一段時(shí)間以后,持有者Bob如果想退出Plasma Cash 子鏈,那么就需要向以太坊合約也就是 RootChain證明,他確實(shí)擁有這一筆資產(chǎn).

3.4.1 資產(chǎn)擁有證明

這個(gè)思路和 UTXO 的思路是一樣的,Bob能證明這筆資產(chǎn)是從哪里轉(zhuǎn)給我的即可.具體見[RootChain.sol]()中的startExit函數(shù). 其思路非常簡單,證明

  • 這筆資產(chǎn)來自哪里(在哪 M塊中轉(zhuǎn)移到了 Alice 手中)
  • 經(jīng)過 Alice 簽名轉(zhuǎn)移給了Bob(在N塊中 Alice 做了簽名給我)
    具體看代碼 startExit

    /** @dev Starts the procedure for withdrawal of the deposit from the system.
     *  @param previousTx Penultimate deposit transaction.
     *  @param previousTxProof Proof of inclusion of a penultimate transaction in a Smart Plasma block.
     *  @param previousTxBlockNum The number of the block in which the penultimate transaction is included.
     *  @param lastTx Last deposit transaction.
     *  @param lastTxProof Proof of inclusion of a last transaction in a Smart Plasma block.
     *  @param lastTxBlockNum The number of the block in which the last transaction is included.
     */
    function startExit(
        bytes previousTx,
        bytes previousTxProof,
        uint256 previousTxBlockNum,
        bytes lastTx,
        bytes lastTxProof,
        uint256 lastTxBlockNum
    )
        public
    {
        Transaction.Tx memory prevDecodedTx = previousTx.createTx();
        Transaction.Tx memory decodedTx = lastTx.createTx();
        // 證明在 prevBlock的時(shí)候 Alice 擁有資產(chǎn) uid
        require(previousTxBlockNum == decodedTx.prevBlock);
        require(prevDecodedTx.uid == decodedTx.uid);
        //amount 不變,證明資產(chǎn)不可分割
        require(prevDecodedTx.amount == decodedTx.amount);
        //Alice 確實(shí)簽名轉(zhuǎn)移給了我,并且交易是相鄰的兩筆交易
        require(prevDecodedTx.newOwner == decodedTx.signer);
        require(decodedTx.nonce == prevDecodedTx.nonce.add(uint256(1))); //緊挨著的兩筆交易
        //我是 Bob, 我要來拿走這筆資產(chǎn)
        require(msg.sender == decodedTx.newOwner);
        require(wallet[bytes32(decodedTx.uid)] != 0);
    
        bytes32 prevTxHash = prevDecodedTx.hash;
        bytes32 prevBlockRoot = childChain[previousTxBlockNum];
        bytes32 txHash = decodedTx.hash;
        bytes32 blockRoot = childChain[lastTxBlockNum];
    
        require(
            prevTxHash.verifyProof(
                prevDecodedTx.uid,
                prevBlockRoot,
                previousTxProof
            )
        );
        require(
            txHash.verifyProof(
                decodedTx.uid,
                blockRoot,
                lastTxProof
            )
        );
    
        /// Record the exit tx.
        require(exits[decodedTx.uid].state == 0);
        require(challengesLength(decodedTx.uid) == 0);
    
        exits[decodedTx.uid] = exit({
            state: 2,
            exitTime: now.add(challengePeriod),
            exitTxBlkNum: lastTxBlockNum,
            exitTx: lastTx,
            txBeforeExitTxBlkNum: previousTxBlockNum,
            txBeforeExitTx: previousTx
        });
    
        StartExit(prevDecodedTx.uid, previousTxBlockNum, lastTxBlockNum);
    }

    代碼的前一半都是在用來證明在lastTxBlockNum的時(shí)候,資產(chǎn) uid 歸Bob所有.
    然后后一半就是提出來,Bob想把資產(chǎn) uid 提走. 我的這個(gè)想法會(huì)暫時(shí)保存在合約中,等待別人來挑戰(zhàn).

3.4.2 等待其他人來挑戰(zhàn)我

有了以上信息, 就可以證明在 N 塊時(shí),這筆資產(chǎn)歸Bob所用.但是這肯定不夠,無法證明現(xiàn)在資產(chǎn)仍然屬于Bob,也無法證明Alice 沒有在 M 塊以后再給別人.
更加不能證明在 M 塊的時(shí)候 Alice 真的是 uid 的擁有者?
這些問題,看起來很難回答,其實(shí)思路也很簡單.
這個(gè)思路和雷電網(wǎng)絡(luò)中解決問題的辦法是一樣的, 讓這筆資產(chǎn)的利益攸關(guān)者站出來舉證.
比如: 如果 Carol能夠舉證這筆資產(chǎn)Bob 后來又轉(zhuǎn)移給了 Carol, 那么實(shí)際上 Bob 就是在雙花.
具體的挑戰(zhàn)以及迎戰(zhàn)代碼比較復(fù)雜,但是這也是 Plasma Cash 的核心安全性所在.如果沒有這些,所有的參與者都將無法保證自己的權(quán)益.

//challengeExit 挑戰(zhàn)資產(chǎn)uid 其實(shí)不屬于 Bob
  /** @dev Challenges a exit.
     *  @param uid Unique identifier of a deposit.
     *  @param challengeTx Transaction that disputes an exit.
     *  @param proof Proof of inclusion of the transaction in a Smart Plasma block.
     *  @param challengeBlockNum The number of the block in which the transaction is included.
     */
    function challengeExit(
        uint256 uid,
        bytes challengeTx,
        bytes proof,
        uint256 challengeBlockNum
    )
        public
    {
        require(exits[uid].state == 2);

        Transaction.Tx memory exitDecodedTx = (exits[uid].exitTx).createTx();
        Transaction.Tx memory beforeExitDecodedTx = (exits[uid].txBeforeExitTx).createTx();
        Transaction.Tx memory challengeDecodedTx = challengeTx.createTx();

        require(exitDecodedTx.uid == challengeDecodedTx.uid);
        require(exitDecodedTx.amount == challengeDecodedTx.amount);

        bytes32 txHash = challengeDecodedTx.hash;
        bytes32 blockRoot = childChain[challengeBlockNum];

        require(txHash.verifyProof(uid, blockRoot, proof));

        // test challenge #1 & test challenge #2 最后一筆交易后面又進(jìn)行了其他交易, Bob 在進(jìn)行雙花
        if (exitDecodedTx.newOwner == challengeDecodedTx.signer &&
        exitDecodedTx.nonce < challengeDecodedTx.nonce) {
            delete exits[uid];
            return;
        }

        // test challenge #3, 雙花了,  Alice 給了兩個(gè)人,并且挑戰(zhàn)者 Carol的BlockNumer 更小,也就是發(fā)生的更早.
        if (challengeBlockNum < exits[uid].exitTxBlkNum &&
            (beforeExitDecodedTx.newOwner == challengeDecodedTx.signer &&
            challengeDecodedTx.nonce > beforeExitDecodedTx.nonce)) {
            delete exits[uid];
            return;
        }

        // test challenge #4   在 M塊之前,還有一筆交易,Alice 需要證明自己在 M 塊確實(shí)擁有 uid
        if (challengeBlockNum < exits[uid].txBeforeExitTxBlkNum ) {
            exits[uid].state = 1;
            addChallenge(uid, challengeTx, challengeBlockNum);
        }

        require(exits[uid].state == 1);

        ChallengeExit(uid);
    }

//Bob應(yīng)戰(zhàn),再次舉證,實(shí)際上這個(gè)過程就是要不斷的追加證據(jù),將所有的交易連起來,最終證明 Alice 在 M塊確實(shí)擁有 uid
 /** @dev Answers a challenge exit.
     *  @param uid Unique identifier of a deposit.
     *  @param challengeTx Transaction that disputes an exit.
     *  @param respondTx Transaction that answers to a dispute transaction.
     *  @param proof Proof of inclusion of the respond transaction in a Smart Plasma block.
     *  @param blockNum The number of the block in which the respond transaction is included.
     */
    function respondChallengeExit(
        uint256 uid,
        bytes challengeTx,
        bytes respondTx,
        bytes proof,
        uint blockNum
    )
        public
    {
        require(challengeExists(uid, challengeTx));
        require(exits[uid].state == 1);

        Transaction.Tx memory challengeDecodedTx = challengeTx.createTx();
        Transaction.Tx memory respondDecodedTx = respondTx.createTx();

        require(challengeDecodedTx.uid == respondDecodedTx.uid);
        require(challengeDecodedTx.amount == respondDecodedTx.amount);
        require(challengeDecodedTx.newOwner == respondDecodedTx.signer);
        require(challengeDecodedTx.nonce.add(uint256(1)) == respondDecodedTx.nonce);
        require(blockNum < exits[uid].txBeforeExitTxBlkNum);

        bytes32 txHash = respondDecodedTx.hash;
        bytes32 blockRoot = childChain[blockNum];

        require(txHash.verifyProof(uid, blockRoot, proof));

        removeChallenge(uid, challengeTx);

        if (challengesLength(uid) == 0) {
            exits[uid].state = 2;
        }

        RespondChallengeExit(uid);
    }
3.4.3 挑戰(zhàn)期過了, Bob 拿回資產(chǎn) uid

挑戰(zhàn)期過后,Bob 在Mediator.sol 中提出將資產(chǎn)退回到以太坊中

 /** @dev withdraws deposit from Smart Plasma.
     *  @param prevTx Penultimate deposit transaction.
     *  @param prevTxProof Proof of inclusion of a penultimate transaction in a Smart Plasma block.
     *  @param prevTxBlkNum The number of the block in which the penultimate transaction is included.
     *  @param txRaw lastTx Last deposit transaction.
     *  @param txProof Proof of inclusion of a last transaction in a Smart Plasma block.
     *  @param txBlkNum The number of the block in which the last transaction is included.
     */
    function withdraw(
        bytes prevTx,
        bytes prevTxProof,
        uint prevTxBlkNum,
        bytes txRaw,
        bytes txProof,
        uint txBlkNum
    )
        public
    {
        bytes32 uid = rootChain.finishExit(
            msg.sender,
            prevTx,
            prevTxProof,
            prevTxBlkNum,
            txRaw,
            txProof,
            txBlkNum
        );

        entry invoice = cash[uid];

        Token token = Token(invoice.currency);
        token.transfer(msg.sender, invoice.amount); /// 真正的資產(chǎn)轉(zhuǎn)移

        delete(cash[uid]); 
    }

RootChain 再次驗(yàn)證

 /** @dev Finishes the procedure for withdrawal of the deposit from the system.
     *       Can only call the owner. Usually the owner is the mediator contract.
     *  @param account Account that initialized the deposit withdrawal.
     *  @param previousTx Penultimate deposit transaction.
     *  @param previousTxProof Proof of inclusion of a penultimate transaction in a Smart Plasma block.
     *  @param previousTxBlockNum The number of the block in which the penultimate transaction is included.
     *  @param lastTx Last deposit transaction.
     *  @param lastTxProof Proof of inclusion of a last transaction in a Smart Plasma block.
     *  @param lastTxBlockNum The number of the block in which the last transaction is included.
     */
    function finishExit(
        address account,
        bytes previousTx,
        bytes previousTxProof,
        uint256 previousTxBlockNum,
        bytes lastTx,
        bytes lastTxProof,
        uint256 lastTxBlockNum
    )
        public
        onlyOwner
        returns (bytes32)
    {
        Transaction.Tx memory prevDecodedTx = previousTx.createTx();
        Transaction.Tx memory decodedTx = lastTx.createTx();

        require(previousTxBlockNum == decodedTx.prevBlock);
        require(prevDecodedTx.uid == decodedTx.uid);
        require(prevDecodedTx.amount == decodedTx.amount);
        require(prevDecodedTx.newOwner == decodedTx.signer);
        require(account == decodedTx.newOwner);

        bytes32 prevTxHash = prevDecodedTx.hash;
        bytes32 prevBlockRoot = childChain[previousTxBlockNum];
        bytes32 txHash = decodedTx.hash;
        bytes32 blockRoot = childChain[lastTxBlockNum];

        require(
            prevTxHash.verifyProof(
                prevDecodedTx.uid,
                prevBlockRoot,
                previousTxProof
            )
        );

        require(
            txHash.verifyProof(
                decodedTx.uid,
                blockRoot,
                lastTxProof
            )
        );

        require(exits[decodedTx.uid].exitTime < now); //挑戰(zhàn)期過了
        require(exits[decodedTx.uid].state == 2); //并且沒有人挑戰(zhàn)或者我都給出了合適的證據(jù)
        require(challengesLength(decodedTx.uid) == 0);

        exits[decodedTx.uid].state = 3;

        delete(wallet[bytes32(decodedTx.uid)]);

        FinishExit(decodedTx.uid);

        return bytes32(decodedTx.uid);
    }

4. Plasma Cash 中的退出示例

Plasma Cash 合約解讀

5. 其他問題

    1. 為什么 Plasma Cash 中的資產(chǎn)是不可分割的?
      進(jìn)入 rootchain 的資產(chǎn)類似于比特幣的 UTXO 模型,但是是不可分割的,這個(gè)可以通過交易驗(yàn)證時(shí)每次都要求 amount 不變可以得出.
    1. operator 的作用是什么
      operator 負(fù)責(zé)將子鏈中的交易證據(jù)(默克爾樹)提交證明到以太坊主鏈中.
    1. operator 是否必須可信任的?
      是的. 雖然 operator 不能將他人資產(chǎn)隨意轉(zhuǎn)移,但是卻可以阻止他人資產(chǎn)轉(zhuǎn)移.也就是說無法從 Plasma 子鏈中退回到以太坊中.
      當(dāng)然這部分是可以改進(jìn),降低 operator 作惡帶來的風(fēng)險(xiǎn).
    1. operator 是否可以是一個(gè)合約呢?
      是的. 如果 operator 是一個(gè) Pos 共識(shí)合約,那么可以降低問題3中的風(fēng)險(xiǎn)
添加微信(cdong1024),加入?yún)^(qū)塊鏈開發(fā)者技術(shù)交流群
蟲洞社區(qū):https://www.uzanapp.com/

Plasma Cash 合約解讀

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI