TL; DR: necesito calcular la dirección de una cadena de clave pública determinada en Solidity.
Actualmente estoy tratando de descubrir cómo calcular la dirección a partir de una clave pública, dada como una cadena (o bytes) en solidez. Estoy fallando hasta ahora para hacerlo bien. Como hay varios pasos involucrados y Solidity no es el más fácil de depurar, quería preguntar aquí para ver si hay algún error en mi proceso de pensamiento.
Así que aquí va:
Tengo la clave dada como bytes o como una representación de cadena. Bytes me parece bastante sencillo, pero cuando se habla de la representación de cadenas, no estoy muy seguro de la forma correcta de codificar/decodificar. De todos modos, cuando persiste una clave, la representación de cadenas me parece más habitual, por lo que este es el enfoque principal aquí. Para fines de prueba, generé un par de claves en Java/Web3j como este:
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());
Esto ahora me genera un par de claves aleatorias y genera claves con formato hexadecimal y la dirección asociada. Usemos el siguiente ejemplo:
Public key: 3b88b538dff7db813b6c8be6bfce81f6dd9d820213fe9211e9f5a631c360c7ddbb26690ae40eac62e0b5aaf2d8a5c4287e3c383fc1c00916ce12e354e1eb12eb
Address: 0x622cf04ee8659bc45d76def393077ddcc5396761
Ahora, para calcular la dirección de una clave dada como una cadena, tendré que codificar la clave y usar los últimos 20 bytes ( https://en.wikipedia.org/wiki/Ethereum#Addresses ). Esto también está en línea con el libro Ethereum: https://github.com/ethereumbook/ethereumbook/blob/develop/04keys-addresses.asciidoc#ethereum-addresses
En Solidity, el hash a través de keccack256 requiere una bytes
entrada, por lo que convierto mi cadena en bytes (aquí es donde usaría la entrada de bytes si la clave se proporcionara como tal):
bytes memory bytesKey;
bytesKey = abi.encodePacked(strKey);
El resultado de eso (por ejemplo, si se devuelve como un parámetro de retorno de una función de Solidez) para la clave de ejemplo anterior es
bytes: 0x3362383862353338646666376462383133623663386265366266636538316636646439643832303231336665393231316539663561363331633336306337646462623236363930616534306561633632653062356161663264386135633432383765336333383366633163303039313663653132653335346531656231326562
Ahora, tendré que calcular la dirección real a partir de eso. El primer paso aquí sería calcular el hash a través de keccack256()
. Haciendo esto me da con la tecla de ejemplo
bytes32: 0xb79316bedc9b38a71ead3f08f90433a4f4ae7a2ba5f71ef4fc85827c884f1b5d
La dirección de este debe ser los 20 bytes menos significativos, en otras palabras, los 40 caracteres más a la derecha en este. El problema es que esos no equivalen a la dirección calculada por Web3j en primer lugar. Pero vayamos paso a paso.
Solidity address()
espera una byte20
entrada. Pero si lo convertiría explícitamente en una bytes20
variable como bytes20 x = bytes20(keyHash);
, obtendría los 20 bytes más significativos. Encontré una respuesta en Obtener dirección de clave pública en Solidity que me permite leer correctamente los últimos 20 bytes (40 caracteres) del hash:
assembly {
mstore(0, keyHash)
addr := mload(0)
}
Funciona bien como se anuncia, pero aún así el resultado ( 0xf90433A4F4aE7A2ba5f71ef4Fc85827c884F1b5d
) no coincide con la dirección esperada0x622cf04ee8659bc45d76def393077ddcc5396761
Mi suposición es que en algún lugar se usó incorrectamente una conversión o codificación. De todos modos, los documentos de Solidity y los recursos de Ethereum en general, no puedo encontrar ninguna explicación explícita de todo esto. Puede alguien ayudarme con esto?
Para fines de prueba, aquí está el código completo de Solidity. Tenga en cuenta que en el código real, esa cadena clave se pasará como un parámetro y no se codificará como se hace aquí:
// 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;
}
}
El problema es que abi.encodePacked(key)
interpreta la clave como un valor de cadena, no como un valor hexadecimal. Si bien hay formas de convertir una cadena hexadecimal en bytes en Solidity , es mucho más fácil declararla como bytes en primer lugar:
bytes memory publicKey = hex"3b88b538dff7db813b6c8be6bfce81f6dd9d820213fe9211e9f5a631c360c7ddbb26690ae40eac62e0b5aaf2d8a5c4287e3c383fc1c00916ce12e354e1eb12eb";
Luego, simplemente puede enviar el resultado a una dirección para obtener la dirección del hash Keccak-256 completo:
function test() public pure returns (address) {
bytes memory publicKey = hex"3b88b538dff7db813b6c8be6bfce81f6dd9d820213fe9211e9f5a631c360c7ddbb26690ae40eac62e0b5aaf2d8a5c4287e3c383fc1c00916ce12e354e1eb12eb";
bytes32 hash = keccak256(publicKey);
return address(uint160(uint256(hash)));
}
Llamar a esto da 0x622CF04ee8659bC45d76deF393077Ddcc5396761
como se esperaba.
Majd TL
Morten
xenonita
hex"abcd..."
entonces no se puede usar. Desafortunadamente, ninguna funciónhex()
parece existir. ¿Uno tiene que pasar por la extensa transcodificación "manual" en la publicación vinculada por usted o hay otra forma?Morten
bytes memory
? No estoy muy familiarizado con Web3j, pero supongo que tiene una forma de hacerlo, y convertir una cadena hexadecimal en bytes debería ser mucho más fácil (y más eficiente) en Java que en Solidity.xenonita
Majd TL
xenonita