署名の検証のスマートコントラクト実装

## 検証用Contractの実装
解説は後述
```javascript
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

contract VerifySignature {
    /* 1. Get message hash to sign */
    function getMessageHash(string memory _message) public pure returns (bytes32) {
        return keccak256(abi.encodePacked(_message));
    }

	/* 2. Sign with message hash and */

    /* 3. Sign message hash */
    function getEthSignedMessageHash(bytes32 _messageHash) public pure returns (bytes32) {
        return keccak256(
    		abi.encodePacked("\x19Ethereum Signed Message:\n32", _messageHash)
        );
    }

    /* 4. Verify signature
	    _signer = 0xB273216C05A8c0D4F0a4Dd0d7Bae1D2EfFE636dd
		  _message = "coffee and donuts"
		  _signature = 0x993dab3dd91f5c6dc28e17439be475478f5635c92a56e17e82349d3fb2f166196f466c0b4e0c146f285204f0dcb13e5ae67bc33f4b888ec32dfe0a063e8f3f781b
    */
    function verify(
        address _signer,
        string memory _message,
        bytes memory _sig // Signature that was created using MetaMask
    ) public pure returns (bool) {
        bytes32 messageHash = getMessageHash(_message); // Message hash
        bytes32 ethSignedMessageHash = getEthSignedMessageHash(messageHash); // Prefixed message hash

        return recoverSigner(ethSignedMessageHash, _sig) == _signer; // Compare signrer addresses
    }

    function recoverSigner(
        bytes32 _ethSignedMessageHash,
        bytes memory _sig
    ) public pure returns (address) {
        (bytes32 r, bytes32 s, uint8 v) = splitSignature(_sig);

        // ecrecover(<prefixed_message_hash>, r, s, v) returns signer that was used to sign the message
        return ecrecover(_ethSignedMessageHash, v, r, s);
    }

    function splitSignature(bytes memory sig) public pure returns (bytes32 r, bytes32 s, uint8 v) {
        require(sig.length == 65, "invalid signature length");
        assembly {
            r := mload(add(sig, 32)) // first 32 bytes, after the length prefix
            s := mload(add(sig, 64)) // second 32 bytes
            v := byte(0, mload(add(sig, 96))) // final byte (first byte of the next 32 bytes)
        }
    }
}
```

### `"\\x19Ethereum Signed Message:\\n32"`
https://qiita.com/uskayyyyy/items/ffb581c9943d8b152167

### `ecrecover()`
https://qiita.com/uskayyyyy/items/b4c37c73cc6ed0d84500

### `assembly{}, mload(), add()`
https://qiita.com/uskayyyyy/items/e42b626a1f26fb2e8b72


## 署名と検証の流れ
### Sign (署名)
1. 署名に使用するメッセージ(string)の作成
2. メッセージのハッシュ化
    (`getMessageHash()`)
3. MetaMaskでメッセージハッシュを署名
### Verify (検証)
1. メッセージからメッセージhashを作成
    (`getMessageHash()`, `getEthSignedMessageHash()`)
2. signerをsignatureとhashから導き出す
    (`recoverSigner()`)
3. 2で導き出したsignerと検証対象のsignerの一致を調べる

## MetaMaskを用いた署名
今回はJavaScript Consoleで実行
```javascript
ethereum.enable()

const account = "<signer_address>" // your wallet address
const hash = "<message_hash>" // メッセージハッシュ

// ここでMetaMaskウィンドウが開く
ethereum.request({
	method: "personal_sign",
	params: [account, hash]
});
```
<img width="250" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1975528/b6926219-d310-d56c-34ce-e784e6ed875a.png" />

メタマスクで署名(Sign)を実行後、Promiseで署名(Signature)が返る。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1975528/2210fdea-f278-06c3-0a42-d941d7e9217e.png" />


## Ethers.jsによる署名と検証も可能
```javascript
// Sign
const sig = await signer.signMessage(msg)
// Verify
ethers.utils.verifyMessage(msg, sig)
```

## 参考
https://youtu.be/vYwYe-Gv_XI

https://solidity-by-example.org/signature/

https://youtu.be/vhUjCLYlnMM