--- title: 'Solidity WTF 103 37 單元 數字簽名 Signature' lang: zh-tw --- Solidity WTF 103 37 單元 數字簽名 Signature === :::info :date: 2024/10/13 ::: [TOC] # 數字簽名 Signature 以太坊使用的數字簽名算法叫雙橢圓曲線數字簽名算法(`ECDSA`)。 ## ECDSA 合約 有私鑰與公鑰兩種,但加解密會運用到消息。 ### 创建签名 #### 打包消息 `ECDSA`合約標準中,簽名的消息是`keccak256`哈希,為`bytes32`類型。可以把任何想要簽名的內容使用`abi.encodePacked()`打包,然後用`keccak256()`計算哈希,作為`消息`。 ```javascript= /* * 將 mint 地址(address 類型)和 tokenId(uint256 類型)拼成消息 msgHash * _account: 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4 * _tokenId: 0 * 對應的消息 msgHash: 0x1bf2c0ce4546651a1a2feb457b39d891a6b83931cc2454434f39961345ac378c */ function getMessageHash(address _account, uint256 _tokenId) public pure returns(bytes32){ return keccak256(abi.encodePacked(_account, _tokenId)); } ``` #### 計算以太坊簽名消息 消息可以是能被執行的交易,為避免惡意交易,EIP191提倡在消息前加上"\x19Ethereum Signed Message:\n32"字符,作為以太坊簽名消息,函數處理後,不能執行交易。 ```javascript= function toEthSignedMessageHash(bytes32 hash) public pure returns (bytes32) { return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash)); } ``` ### 驗證簽名 > r: 一個32字節的值,代表簽名的一部份,通常對應於簽名過程中隨機選取的點。 > s: 另一個32字節的值,與r共同用於驗證簽名的唯一性和有效性。 > v: 1字節,通常為27或28,表示recovery id,幫助確定簽名使用的兩個可能公鑰中的哪一個,從而達到恢復出簽名者的地址。 ```javascript! function recoverSigner(bytes32 _msgHash, bytes memory _signature) internal pure returns (address){ require(_signature.length == 65, "invalid signature length"); bytes32 r; bytes32 s; uint8 v; assembly { r := mload(add(_signature, 0x20)) s := mload(add(_signature, 0x40)) v := byte(0, mload(add(_signature, 0x60))) } return ecrecover(_msgHash, v, r, s); } ``` 之後去驗證簽名地址是否正確,如果正確則返回true 即可以完成簽名 ECDSA發放白名單的方式,因為是鍊下的緣故不需gas,因此這種白名單發放模式比Merkle Tree還要經濟便宜。