TL;DR: мне нужно вычислить адрес из заданной строки открытого ключа в Solidity.
В настоящее время я пытаюсь выяснить, как вычислить адрес из открытого ключа, заданного в виде строки (или байтов) в твердости. Я пока не могу понять это правильно. Поскольку требуется несколько шагов, а отладка Solidity не самая простая, я хотел спросить здесь, есть ли какие-либо ошибки в моем мыслительном процессе.
Итак, вот оно:
У меня есть ключ либо в виде байтов, либо в виде строкового представления. Байты кажутся мне довольно простыми, но, говоря о строковом представлении, я не слишком уверен в правильном способе кодирования/декодирования. Во всяком случае, при сохранении ключа представление String кажется мне более привычным, поэтому здесь основное внимание уделяется этому. В целях тестирования я создал пару ключей в Java/Web3j следующим образом:
ECKeyPair keyPair = Keys.createEcKeyPair();
Credentials c = Credentials.create(keyPair);
System.out.println("Private key: " + keyPair.getPrivateKey().toString(16));
System.out.println("Public key: " + keyPair.getPublicKey().toString(16));
System.out.println("Address: " + credentials.getAddress());
Теперь это генерирует мне случайную пару ключей и выводит ключи в шестнадцатеричном формате и соответствующий адрес. Давайте воспользуемся следующим примером:
Public key: 3b88b538dff7db813b6c8be6bfce81f6dd9d820213fe9211e9f5a631c360c7ddbb26690ae40eac62e0b5aaf2d8a5c4287e3c383fc1c00916ce12e354e1eb12eb
Address: 0x622cf04ee8659bc45d76def393077ddcc5396761
Теперь, чтобы вычислить адрес ключа, заданного в виде строки, мне придется хэшировать ключ и использовать его последние 20 байтов ( https://en.wikipedia.org/wiki/Ethereum#Addresses ). Это также соответствует книге Ethereum: https://github.com/ethereumbook/ethereumbook/blob/develop/04keys-addresses.asciidoc#ethereum-addresses.
В Solidity хеширование через keccack256 требует bytes
ввода, поэтому я конвертирую свою строку в байты (здесь я бы просто использовал ввод байтов, если бы ключ был предоставлен как один):
bytes memory bytesKey;
bytesKey = abi.encodePacked(strKey);
Результатом этого (например, если он возвращается как возвращаемый параметр из функции Solidity) для приведенного выше примера ключа является
bytes: 0x3362383862353338646666376462383133623663386265366266636538316636646439643832303231336665393231316539663561363331633336306337646462623236363930616534306561633632653062356161663264386135633432383765336333383366633163303039313663653132653335346531656231326562
Теперь мне нужно вычислить фактический адрес из этого. Первым шагом здесь будет вычисление хэша через keccack256()
. Выполнение этого дает мне пример ключа
bytes32: 0xb79316bedc9b38a71ead3f08f90433a4f4ae7a2ba5f71ef4fc85827c884f1b5d
Адрес этого должен быть 20 младшими байтами, другими словами, 40 крайними правыми символами в этом. Проблема в том, что они не равны адресу, вычисленному Web3j. Но давайте шаг за шагом.
Solidity address()
ожидает byte20
ответа. Но если бы я явно привел его в bytes20
переменную типа bytes20 x = bytes20(keyHash);
, я бы получил 20 самых значащих байтов. Я нашел ответ в разделе «Получить адрес из открытого ключа в Solidity» , который позволяет мне правильно прочитать последние 20 байт (40 символов) хэша:
assembly {
mstore(0, keyHash)
addr := mload(0)
}
Работает нормально, как рекламируется, но результат ( 0xf90433A4F4aE7A2ba5f71ef4Fc85827c884F1b5d
) не соответствует ожидаемому адресу.0x622cf04ee8659bc45d76def393077ddcc5396761
Я предполагаю, что где-то неправильно использовалось преобразование или кодирование. Во всяком случае, в документации Solidity и ресурсах Ethereum в целом я не могу найти явного объяснения всему этому. Кто-нибудь может мне с этим помочь?
В целях тестирования, вот весь код Solidity. Обратите внимание, что в реальном коде эта ключевая строка будет передаваться как параметр, а не жестко закодирована, как это сделано здесь:
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.5;
contract KeyTest {
function test() public returns (address) {
string memory key = "3b88b538dff7db813b6c8be6bfce81f6dd9d820213fe9211e9f5a631c360c7ddbb26690ae40eac62e0b5aaf2d8a5c4287e3c383fc1c00916ce12e354e1eb12eb";
string memory addrOriginal = "0x622cf04ee8659bc45d76def393077ddcc5396761";
// convert string to bytes
bytes memory bytesKey = abi.encodePacked(key);
//results in bytesKey: 0x3362383862353338646666376462383133623663386265366266636538316636646439643832303231336665393231316539663561363331633336306337646462623236363930616534306561633632653062356161663264386135633432383765336333383366633163303039313663653132653335346531656231326562
bytes32 keyHash = keccak256(bytesKey);
//bytes32: 0xb79316bedc9b38a71ead3f08f90433a4f4ae7a2ba5f71ef4fc85827c884f1b5d
address addr;
address addr;
assembly {
mstore(0, keyHash)
addr := mload(0)
}
// addr: 0xf90433A4F4aE7A2ba5f71ef4Fc85827c884F1b5d
return addr;
}
}
Проблема в том, что abi.encodePacked(key)
ключ интерпретируется как строковое значение, а не как шестнадцатеричное значение. Хотя в Solidity есть способы преобразовать шестнадцатеричную строку в байты , в первую очередь гораздо проще объявить ее как байты:
bytes memory publicKey = hex"3b88b538dff7db813b6c8be6bfce81f6dd9d820213fe9211e9f5a631c360c7ddbb26690ae40eac62e0b5aaf2d8a5c4287e3c383fc1c00916ce12e354e1eb12eb";
Затем вы можете просто привести результат к адресу, чтобы получить адрес из полного хэша Keccak-256:
function test() public pure returns (address) {
bytes memory publicKey = hex"3b88b538dff7db813b6c8be6bfce81f6dd9d820213fe9211e9f5a631c360c7ddbb26690ae40eac62e0b5aaf2d8a5c4287e3c383fc1c00916ce12e354e1eb12eb";
bytes32 hash = keccak256(publicKey);
return address(uint160(uint256(hash)));
}
Вызов этого приводит к 0x622CF04ee8659bC45d76deF393077Ddcc5396761
ожидаемому результату.
Маджд ТЛ
Мортен
Ксенонит
hex"abcd..."
тогда нельзя использовать. К сожалению, никакой функции,hex()
кажется, не существует. Нужно ли проходить обширное «ручное» перекодирование в сообщении, на которое вы ссылаетесь, или есть другой способ?Мортен
bytes memory
? Я не слишком хорошо знаком с Web3j, но я предполагаю, что у него есть способ сделать это, и преобразование шестнадцатеричной строки в байты должно быть намного проще (и эффективнее) в Java, чем в Solidity.Ксенонит
Маджд ТЛ
Ксенонит