智能合約所有者和發送者的公鑰
我對乙太坊很陌生。我想知道是否可以寫一個契約,我可以知道
Owner
’s public key 和’Sender’s public key ?
這不可能直接:公鑰不能直接用於合約。合約只有發件人的地址,該地址是從他們的公鑰派生的。
但是,合約有可能以另一種方式進行推導,因此如果您將公鑰作為參數傳遞給它,它可以找出它對應的地址。
儘管合約看不到公鑰,但技術上可以使用在合約外執行的程式碼從區塊鍊和/或網路中檢索它,前提是該地址實際上至少被使用過一次。這是可能的,因為每次發送交易時,檢查簽名都涉及首先從簽名中恢復公鑰,然後計算地址並檢查它是否匹配。
因此,您(或其他人)可以做的一件事是創建一個目錄合約,將地址列表保存到公鑰,並執行一個機器人來查詢失去的地址,從區塊鏈中恢復公鑰,然後發送它到目錄契約。
如果您不想自己執行機器人,您可以修改目錄合約以管理每個失去的公鑰的賞金。因此,只要您的合約需要公鑰,您的合約就會向目錄合約發送少量 ETH,而網際網路上的某個不知名的人會查找公鑰,將其發送到目錄合約並索取賞金。
我看到這種類型的問題出現在很多不同的地方,並且(不幸的是)經常被回答為“你不需要那個”或“你為什麼想要那個”。
嚴格來說區塊鏈,這種類型的答案是正確的,假設所有“通信”都是通過向區塊鏈發送簽名交易來完成的。在這裡,知道公鑰通常是沒有用的。
無論如何,如果有人將區塊鏈(讀作“乙太坊”)視為分佈式、公共、可驗證的記錄分類賬(以及智能合約的狀態),而不是應用程序/服務的所有數據的基礎,那麼這個問題突然變得很重要的意義。
一個場景可能是兩個通信夥伴位於鏈下設備(伺服器、手機、筆記型電腦……)上。兩者都有一個用於辨識和加密/解密/簽名的密鑰對。然後分類帳將註冊兩個使用者的地址(公鑰雜湊的一部分),然後將其用作(可證明的)標識符。然後通信將完全脫鏈,即直接在兩個使用者的設備之間進行。
如果使用者 A 現在收到來自使用者 B 的簽名消息,A 可以輕鬆驗證該消息沒有經過修改,並且它實際上來自與 A 的地址相關聯的私鑰的所有者。(A 將派生 B 的公鑰從消息的簽名中計算出地址,並驗證它是 B 的實際地址)。
無論如何,如果 A 和 B 還沒有聯繫,並且 A 想向 B 發送 ENCRYPTED 消息,那麼 A 無法僅通過知道 B 的地址來了解 B 的公鑰。
所以使用者需要在某個地方發布他們的地址到他們的公鑰的映射。這基本上是 SSI(DID 和 DID 文件)背後的想法(部分)。
現在,話雖如此,有一些選擇可以實現這一目標:
- 有點要求每個使用者創建一個公開可用且僅可從使用者地址發現的數字簽名。當我們談論 EC-crypto 時,可以從該簽名中導出公鑰。在沒有特定系統資料庫的情況下執行此操作的一種方法是讓每個使用者以某種方式與乙太坊進行交易。從這裡,您可以獲取從此地址發送/簽名的交易(請參閱愚蠢的隨機範例https://etherscan.io/address/0x39ec1d8f3d431770a8c79b33ead5a2e9e3c02d25)並從那裡獲取簽名和公鑰。
- 創建一個將地址映射到公鑰的公共系統資料庫。可以是具有 to 映射的智能合約,也可以是
address
連結string
到(可能是鏈外)DID 文件的 DID-Registry。例如,參見 uPort 的 ERC1056。要直接為選項 2 提出 MWE,可以執行以下操作:
contract registry { mapping (address => string) private keys; function getKey(address a) public view returns (string) { return keys[a]; } function setKey(string key) public returns (address) { address a = address(keccak256(key)); keys[a] = key; return a; } }
編輯:一起拍了一個實際工作的例子:
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.4; contract KeyRegistry { mapping (address => bytes) private keys; function getKey(address a) public view returns (bytes) { if(keys[a] == 0x0) revert("Error: Unknown address"); return keys[a]; } function setKey(bytes key) public returns (address) { if(key.length == 0) revert("Error: Key cant be empty"); address a = address(uint160(bytes20(key))); if(a != msg.sender) revert("Error: Only the owner of a key can register this key"); if(keys[a] != 0) revert("Error: Key is already registered"); keys[a] = key; return a; } }