DEV Community

auok007
auok007

Posted on

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

Discussion (0)