Skip to content

Multisig Wallet là gì?

Posted on:May 15, 2020

Intro

Thông thường các ví trong blockchain nói chung đều được tạo ra bởi một private key duy nhất.

Tất cả các tài sản hay transaction đều được kiểm soát và ký bởi private key đó.

Điều này đối với cá nhân thì rất tốt, vì nó đảm bảo tính riêng tư và bảo mật, vì chỉ có chủ sở hữu private key mới có toàn quyền các tài sản đó.

Nhưng với một tổ chức, chẳng hạn như các tài sản sau khi ICO, hoặc tài sản chung của công ty thì khác, nếu tài sản này được kiểm soát bởi một người duy nhất thì sẽ có rất nhiều rủi ro có thể xảy ra như:

Để giải quyết các vấn đề trên, Multisig Wallet ra đời.

Multisig wallet

Về bản chất Multisig Wallet là một smart contract trên blockchain.

Multisig là viết gọn của Multi Signature (đa chữ ký).

Một Multisig Wallet có các tính chất:

Ưu nhược điểm của Multisig Wallet

Cái gì cũng có 2 mặt, bảo mật và sự tiện dụng luôn luôn là 2 mặt đối lập với nhau, với Multisig Wallet cũng vậy.

Ưu điểm

Nhược điểm

Trong trường hợp này, 2FA có thể là một giải pháp hữu hiệu để lưu backup code cho tài khoản trên thiết bị, nếu ta bị mất thiết bị vẫn có thể lấy lại được backup code, đồng nghĩa với việc lấy lại được tài khoản.

Xây dựng Minimum Multisig Wallet

Sau đây ta sẽ xây dựng một ví dụ nhỏ để nắm được một Multisig Wallet có những phần nào và logic trong đó được triển khai ra sao.

Nền tảng sử dụng là Ethereum, ngôn ngữ sử dụng ở đây là Solidity, phiên bản 0.5.0.

Các bạn có thể lên Remix để gõ code trực tiếp mà không cần cài đặt môi trường.

Immutable List of Owners

Mỗi Multisig Wallet đều có nhiều owner - là những người sẽ bỏ phiếu để đồng ý xem transaction có được phép diễn ra trên ví hay không.

Trong ví dụ này list owners là cố định, nhưng trên thực tế ta hoàn toàn có thể xây dựng logic để thêm vào hay xoá owner đi tuỳ vào business logic của ứng dụng.

pragma solidity 0.5.0;

contract MultiSigWallet {
    address[] public owners;  // immutable state

    constructor(address[] memory owners_) public {
        owners = owners_;
    }
}

Off-Chain Consensus

Trong ví dụ này, một transaction chỉ được diễn ra khi tất cả các owner đều đồng thuận.

Sự đồng thuận giữa các owner được thực hiện off-chain thông qua signed message (message đã được ký). Các signed message này có 4 trường:

  1. destination: đây là địa chỉ sẽ nhận Ether.
  2. value: số lượng Ether được gửi.
  3. R, S, V: chữ ký của message.
  4. nonce: số nonce của transaction. Để tránh double spend, mỗi transaction với mỗi tài khoản được đánh một số duy nhất theo thứ tự, gọi là nonce.

Để transaction được thực hiện, tất cả các owners đều phải cung cấp một signed message để xác thực việc đồng ý chuyển value tới destination. Signed message này được tạo ra bằng cách ký offline (có rất nhiều thư viện cung cấp cho ta phương thức ký offline này, ví dụ như web3js hay ethereumjs-tx).

Khi đã có tất cả những signed message này rồi, đầu tiên ta sẽ kiểm tra tính hợp lệ của tất cả những chữ ký này, nếu tất cả ok thì transaction sẽ được thực hiện.

uint256 public nonce;

function transfer(
    address payable destination,
    uint256 value,
    bytes32[] calldata sigR,
    bytes32[] calldata sigS,
    uint8[] calldata sigV
)
    external
{
    bytes32 hash = prefixed(keccak256(abi.encodePacked(
        address(this), destination, value, nonce
    )));

    for (uint256 i = 0; i < owners.length; i++) {
        address recovered = ecrecover(hash, sigV[i], sigR[i], sigS[i]);
        require(recovered == owners[i]);
    }

    nonce += 1;
    destination.transfer(value);
}

ta có contract đầy đủ như sau:

pragma solidity 0.5.0;

contract MutlisigWallet {
    uint256 public nonce;     // (only) mutable state
    address[] public owners;  // immutable state

    constructor(address[] memory owners_) public {
        owners = owners_;
    }

    function transfer(
        address payable destination,
        uint256 value,
        bytes32[] calldata sigR,
        bytes32[] calldata sigS,
        uint8[] calldata sigV
    )
        external
    {
        bytes32 hash = prefixed(keccak256(abi.encodePacked(
            address(this), destination, value, nonce
        )));

        for (uint256 i = 0; i < owners.length; i++) {
            address recovered = ecrecover(hash, sigV[i], sigR[i], sigS[i]);
            require(recovered == owners[i]);
        }

        // If we make it here, all signatures are accounted for.
        nonce += 1;
        destination.transfer(value);
    }

    function () payable external {}

    // Builds a prefixed hash to mimic the behavior of eth_sign.
    function prefixed(bytes32 hash) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked(
            "\x19Ethereum Signed Message:\n32", hash));
    }
}

Các Multisig Wallet nổi tiếng

Không có một chuẩn chung nào cho việc viết Multisig Wallet, tuy nhiên ta có thể tham khảo cách triển khai từ những wallet nổi tiếng đang được sử dụng trên thế giới để có thể tự thiết kế hoặc kế thừa cho implement của mình.

Tham khảo