---
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還要經濟便宜。