“BLUR空投认领解释 – 将证明嵌入交易中”

huangapple go评论64阅读模式
英文:

BLUR airdrop claim explained - embedding a proof into a transaction

问题

最近,我看到Blur有一个依赖于Twitter的空投活动。基本上,你需要发推文,然后就可以选择领取代币。我去分析了这段代码,但对我来说有点复杂。

这是领取函数:

function claim(
    address account,
    uint256 amount,
    bytes32[] memory proof
) external {
    bytes32 leaf = keccak256(abi.encodePacked(account, amount));
    require(!claimed[leaf], "Airdrop already claimed");
    MerkleVerifier._verifyProof(leaf, merkleRoot, proof);
    claimed[leaf] = true;

    require(IERC20(token).transfer(account, amount), "Transfer failed");

    emit Claimed(account, amount);
}

这是Merkle验证器:

pragma solidity 0.8.17;

/**
 * @title MerkleVerifier
 * @dev 用于Merkle树计算的实用函数
 */
library MerkleVerifier {
    error InvalidProof();

    /**
     * @dev 验证Merkle证明
     * @param leaf 叶子节点
     * @param root 根节点
     * @param proof 证明
     */
    function _verifyProof(
        bytes32 leaf,
        bytes32 root,
        bytes32[] memory proof
    ) public pure {
        bytes32 computedRoot = _computeRoot(leaf, proof);
        if (computedRoot != root) {
            revert InvalidProof();
        }
    }

    /**
     * @dev 计算Merkle根节点
     * @param leaf 叶子节点
     * @param proof 证明
     */
    function _computeRoot(
        bytes32 leaf,
        bytes32[] memory proof
    ) public pure returns (bytes32) {
        bytes32 computedHash = leaf;
        for (uint256 i = 0; i < proof.length; i++) {
            bytes32 proofElement = proof[i];
            computedHash = _hashPair(computedHash, proofElement);
        }
        return computedHash;
    }

    function _hashPair(bytes32 a, bytes32 b) private pure returns (bytes32) {
        return a < b ? _efficientHash(a, b) : _efficientHash(b, a);
    }

    function _efficientHash(
        bytes32 a,
        bytes32 b
    ) private pure returns (bytes32 value) {
        assembly {
            mstore(0x00, a)
            mstore(0x20, b)
            value := keccak256(0x00, 0x40)
        }
    }
}

我的理论是,每次用户发推文时,网站会识别这一行为,并嵌入一个特殊的证明(proof)到交易中,然后将该证明与金额和地址一起计算,最后与根证明(root proof)进行验证(唯一的owner函数)。我不明白的是,为什么这个系统无法被欺骗?我们不能只是复制其中一个证明到交易中,从而避免Twitter验证吗?

最重要的是:这个证明是如何创建的?合同如何跟踪Twitter(据我所知,没有Oracle)?

英文:

Recently, I saw that Blur had a twitter dependent airdrop. So basically you had to tweet and then you would get the option to claim tokens. I went to analyze the code, but it is overwhelming for me.

This is the claim function:

function claim(
        address account,
        uint256 amount,
        bytes32[] memory proof
    ) external {
        bytes32 leaf = keccak256(abi.encodePacked(account, amount));
        require(!claimed[leaf], &quot;Airdrop already claimed&quot;);
        MerkleVerifier._verifyProof(leaf, merkleRoot, proof);
        claimed[leaf] = true;

        require(IERC20(token).transfer(account, amount), &quot;Transfer failed&quot;);

        emit Claimed(account, amount);
    }

This is the Merklee Verifier:

pragma solidity 0.8.17;

/**
 * @title MerkleVerifier
 * @dev Utility functions for Merkle tree computations
 */
library MerkleVerifier {
    error InvalidProof();

    /**
     * @dev Verify the merkle proof
     * @param leaf leaf
     * @param root root
     * @param proof proof
     */
    function _verifyProof(
        bytes32 leaf,
        bytes32 root,
        bytes32[] memory proof
    ) public pure {
        bytes32 computedRoot = _computeRoot(leaf, proof);
        if (computedRoot != root) {
            revert InvalidProof();
        }
    }

    /**
     * @dev Compute the merkle root
     * @param leaf leaf
     * @param proof proof
     */
    function _computeRoot(
        bytes32 leaf,
        bytes32[] memory proof
    ) public pure returns (bytes32) {
        bytes32 computedHash = leaf;
        for (uint256 i = 0; i &lt; proof.length; i++) {
            bytes32 proofElement = proof[i];
            computedHash = _hashPair(computedHash, proofElement);
        }
        return computedHash;
    }

    function _hashPair(bytes32 a, bytes32 b) private pure returns (bytes32) {
        return a &lt; b ? _efficientHash(a, b) : _efficientHash(b, a);
    }

    function _efficientHash(
        bytes32 a,
        bytes32 b
    ) private pure returns (bytes32 value) {
        assembly {
            mstore(0x00, a)
            mstore(0x20, b)
            value := keccak256(0x00, 0x40)
        }
    }
}

My theory is that each time a user tweets, the website recognizes this and embeds a special proof in the transaction, which is then computed with the amount and address and finally verified with the root proof(the only owner function). What I don't understand is why can't this system be cheated? Can't we just copy one of the proofs into the transaction and avoid the twitter verfication?

And most importantly: How is the proof created? How can the contract follow twitter(there is no oracle to my knowledge)?

答案1

得分: 3

为什么这个系统不能被欺骗?难道我们不能只是复制一个证明到交易中,避免 Twitter 验证吗?

在这种特定情况下,如果你传递别人的 proof 和你的 account,声明将失败。

bytes32 leaf = keccak256(abi.encodePacked(account, amount));

对于每个输入,得到的 leaf 值都是不同的。因此,一个合格的地址会得到叶子值 0x12,而你的地址会得到叶子值 0x34

现在,_verifyProof() 函数验证了 leafproof组合是否导致了预期的 root

bytes32 computedHash = leaf;
// ...
computedHash = _hashPair(computedHash, proofElement);

由于你的 leaf 与合格者的不同(尽管 proof 是相同的),这个组合会导致一个不同的 root 值,因此此条件将导致回滚:

if (computedRoot != root) {
    revert InvalidProof();
}

如果你传递了他们的 proof 和他们的 account,那么你只是将空投声明到他们的地址 - 而不是你的地址:

// 将代币发送到 `account` - 而不是 `msg.sender`
IERC20(token).transfer(account, amount)
英文:

why can't this system be cheated? Can't we just copy one of the proofs into the transaction and avoid the twitter verfication?

In this specific case, if you pass someone else's proof and your account, the claim fails.

bytes32 leaf = keccak256(abi.encodePacked(account, amount));

The resulting leaf value is different for each input. So an eligible address would result in leaf 0x12 while your address would result in leaf value 0x34.

Now, the _verifyProof() function validates if the combination of leaf and proof leads to the expected root.

bytes32 computedHash = leaf;
// ...
computedHash = _hashPair(computedHash, proofElement);

Since your leaf is different from the eligible (while the proof is the same), the combination leads to a different root value, and this condition reverts:

if (computedRoot != root) {
    revert InvalidProof();
}

And if you pass their proof and their account as well, then you simply claim the airdrop to their address - not to yours:

// sends tokens to `account` - not to `msg.sender`
IERC20(token).transfer(account, amount)

huangapple
  • 本文由 发表于 2023年5月25日 01:02:42
  • 转载请务必保留本文链接:https://go.coder-hub.com/76325884.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定