Obtener dirección de clave pública en Solidity

La dirección son los últimos 20 bytes desha3(publicKey)

En Solidity, usando ensamblaje si es necesario, ¿cómo podría empalmar los primeros 12 bytes sha3(publicKey)y obtener la dirección? Lo que se me ocurrió se queda sin gasolina:

bytes32 b = sha3(publicKey)
address signer;
assembly {
signer := mload(add(b, 0x0c) // skip the first 12 bytes, 0c in hex = 12
}

MLOAD(0XAB) carga la palabra (32 bytes) ubicada en la dirección de memoria 0XAB. ¿Mi función falla porque está tratando de cargar solo 20 bytes? ¿MLOAD puede cargar solo 20 bytes?

Lo que está haciendo aquí es cargar desde la ubicación de la memoria sha3(pk)+12, que es (muy probablemente) un número de 256 bits. Te estás quedando sin gasolina porque la expansión de la memoria a una ubicación tan alta se la come toda.
La forma "habitual" de lograr lo que desea es AND con 0x[12 bytes cero][20 bytes ff]. Una alternativa (lugar común en otras partes de la informática) es cambiar los X bits de entrada a la izquierda, luego X bits a la derecha (sin "rotación"), pero el EVM no tiene operaciones de cambio (todavía).
@NoelMaersk Eso no es lo que hace esa asamblea. add(b, 0x0c)es análoga a la aritmética de punteros. (Le da la ubicación en la memoria 12 bytes después del comienzo de b).

Respuestas (4)

Esto funciona:

return address(keccak256(publicKey) & (2**(8*21)-1));

2**(8*21)-1es solo un truco para obtener 0xFFFFFF... (40 Fs) sin escribirlo. :-)

EDITAR

Como señaló @schnorr, no hay necesidad de la máscara:

return address(keccak256(publicKey));
dirección de retorno (keccak256 (_publicKey)); también funciona, ¿cómo es que no incluye los primeros 12 bytes del hash de clave pública?
Ah, eso también tiene sentido. addresstiene un tamaño de 20 bytes, por lo que no puede almacenar más bytes que eso. Mantiene los bytes menos significativos, como todas las conversiones a números de menor tamaño.
¿La clave pública no es de 64 bytes? ¿Como funciona esto?
Una clave pública en Ethereum es de 32 bytes, pero ¿por qué importa eso? ( keccak256siempre devuelve 32 bytes.)
¡Las claves públicas en Ethereum son de 64 bytes, no de 32! Sin embargo, las claves PRIVADAS son de 32 bytes. Fuente: hackernoon.com/…

De una respuesta a ¿Cómo convertir un byte a una dirección en Solidity? así como una respuesta a ¿Cómo concatenar una matriz de bytes32 [] en una cadena? , se me ocurrió esto para convertir una bytes32 keyHashen una dirección.

function getAddressFromPublicKey(bytes _publicKey) returns (address signer) {
    // Get address from public key
    bytes32 keyHash = keccak256(_publicKey);
    uint result = 0;
    for (uint i = keyHash.length-1; i+1 > 12; i--) {
      uint c = uint(keyHash[i]);
      uint to_inc = c * ( 16 ** ((keyHash.length - i-1) * 2));
      result += to_inc;
    }
    return address(result);
}

En solidez 0.5:

function calculateAddress(bytes memory pub) public pure returns (address addr) {
    bytes32 hash = keccak256(pub);
    assembly {
        mstore(0, hash)
        addr := mload(0)
    }    
}

Esto debería funcionar para Solidity 0.5.x:

bytes32 hash = keccak256(publicKey);
address wallet = address(uint160(bytes20(hash)));