DEV Community

晓道
晓道

Posted on

2 1

ECDSA使用,实现多签 (二)

昨天留下多签的代码还没写,今天还是写了一会会儿,前文在这里:ECDSA使用,实现多签 (一) | 登链社区 | 深入浅出区块链技术 (learnblockchain.cn)
这篇文章有参考一个老外的文章,但是做了一些优化,思路一致。
Signature Replay | Hack Solidity #13 | by Zuhaib Mohammed | Jan, 2022 | CoinsBench (medium.com)

一个简单的多签,运用到一个转账业务中就是,一个老板小李建立了一个多签合约,初始化公司股东,小张,小王,小陈,小马几个人做为操作人,有这几个人同意,就可以通过多签合约给其他人转钱。

首先初始化合约,最多5个操作人,最少3个人同意就可以转:

constructor(address[] memory _operators) public payable {
        owner = msg.sender;
        require(_operators.length <= 5, "Too many operators");
        for (uint8 i = 0; i < _operators.length; i++) {
            operators[i] = _operators[i];
        }
    }
Enter fullscreen mode Exit fullscreen mode

转账函数定义:

function transfer(
        address _to,
        uint256 _amount,
        bytes[] memory sigs
    ) external {
        bytes32 txHash = getTxHash(_to, _amount);

        require(_checkSigs(txHash, sigs, 2), "Only operators can transfer");
        (bool sender, ) = _to.call{value: _amount}("");
        require(sender, "sender failed");
    }
Enter fullscreen mode Exit fullscreen mode

_checkSigs是我们的关键函数,检查签名是不是这个人签出来的。

function _checkSigs(
        bytes32 txhash,
        bytes[] memory sigs,
        uint8 numSigs //
    ) private view returns (bool) {
        uint8 c = 0;
        //emit log_named_uint("sigs len", sigs.length);
        bool[] memory bops = new bool[](operators.length);
        for (uint8 i = 0; i < sigs.length; i++) {
            //emit log_named_bytes("txHash", txHash);
            if (!_findOpt(txhash.recover(sigs[i]), bops)) {
                return false;
            }
            c++;
        }
        //emit log_named_uint("c", c);
        if (c > numSigs) {
            return true;
        } else {
            return false;
        }
    }
Enter fullscreen mode Exit fullscreen mode
function _findOpt(address sigaddr, bool[] memory bops)
        private
        view
        returns (
            bool
        )
    {
        for (uint8 i = 0; i < operators.length; i++) {
            //emit log_named_address("operators", operators[i]);
            if (operators[i] != address(0x0)) {
                if (bops[i] == false) {
                    if (operators[i] == sigaddr) {
                        //emit log_named_address("find", operators[i]);
                        bops[i] = true;
                        //emit log("set true");
                        return true;
                    }
                }
            } else {
                break;
            }
        }
        return false;
    }
Enter fullscreen mode Exit fullscreen mode

为什么这样就可以?
检查签名是不是这个人签出来的,看我前面的ECDSA用法讲解。 ECDSA使用,实现多签 (一) | 登链社区 | 深入浅出区块链技术 (learnblockchain.cn)
相对于老外的代码,我做了一些升级,操作人变成5个,转账时候的签名,可以不按照顺序进行签名对比。
对比网站上其他人的文章,我这个更像一个技术原型,所以我会继续更新,弄一个更偏向业务的合约。测试用例什么的,都在github上,看测试用例,应该更能理解是怎么回事。
更详细的,看我github上的仓库:
SimpleMultiSig

Image of Timescale

🚀 pgai Vectorizer: SQLAlchemy and LiteLLM Make Vector Search Simple

We built pgai Vectorizer to simplify embedding management for AI applications—without needing a separate database or complex infrastructure. Since launch, developers have created over 3,000 vectorizers on Timescale Cloud, with many more self-hosted.

Read more →

Top comments (0)

Postmark Image

Speedy emails, satisfied customers

Are delayed transactional emails costing you user satisfaction? Postmark delivers your emails almost instantly, keeping your customers happy and connected.

Sign up