¿Cómo convertir un uint a bytes en Solidity?

¿Hay una manera fácil de convertir uinta bytesen Solidity?

Respuestas (7)

Parece que ahora lo hay, desde la versión de solidity 0.4.24 puedes usar abi.encodePacked

P.EJ:

uint i = 0;
i_bytes = abi.encodePacked(i);

La alternativa a la respuesta de @eth es usar ensamblado:

function toBytes(uint256 x) returns (bytes b) {
    b = new bytes(32);
    assembly { mstore(add(b, 32), x) }
}

Esto es significativamente más eficiente en el consumo de gas, pero depende del diseño de la memoria interna que utilice el compilador de Solidity. En teoría, esto puede cambiar en el futuro, pero en la práctica debería ser bastante estable.

¿Te importaría explicar qué hace cada línea? ¿Cuál es el propósito de este complemento (b, 32). ¿No podríamos simplemente hacer mstore(b, x)?
@ppoliani, ¿obtuviste una respuesta a tu pregunta? También tengo curiosidad por qué agregamos 32 a b. (cuando b debería estar al comienzo de la dirección de memoria de todos modos).
En realidad, se explica aquí: ethereum.stackexchange.com/questions/98750/…
@ppoliani el bytestipo en Solidity se almacena en la memoria como: 1) primeros 32 bytes = longitud del bytesvalor, 2) luego el bytesvalor en sí. El mstorecódigo de operación almacena 32 bytes, a partir de un cierto desplazamiento (en el valor de bytes para capturar) a una ubicación específica en la memoria. Entonces, la instrucción mstore(add(b, 32), x)se puede traducir en palabras simples como "almacena 32 bytes en la posición x en la memoria, comenzando desde el desplazamiento de 32 bytes en b" (para omitir la longitud y simplemente almacenarse b).

Aquí hay una comparación del gas utilizado en los tres métodos por @NickJohnson, @Eth y @k26dr. He agregado constanta los modificadores de función ya que estas funciones no alteran la cadena de bloques:

pragma solidity ^0.4.2;

contract Test {
    function toBytesNickJohnson(uint256 x) constant returns (bytes b) {
        b = new bytes(32);
        assembly { mstore(add(b, 32), x) }
    }

    function toBytesEth(uint256 x) constant returns (bytes b) {
        b = new bytes(32);
        for (uint i = 0; i < 32; i++) {
            b[i] = byte(uint8(x / (2**(8*(31 - i))))); 
        }
    }

    function toBytesNicolasMassart(uint256 x) constant returns (bytes c) {
        bytes32 b = bytes32(x);
        c = new bytes(32);
        for (uint i=0; i < 32; i++) {
            c[i] = b[i];
        }
    }    
}

Puede ver el costo de la gasolina para ejecutar estas funciones en Remix (Solidity Browser):

ingrese la descripción de la imagen aquí

No hay formas fáciles de convertir nada a bytes. Aquí hay una función:

function toBytes(uint256 x) returns (bytes b) {
    b = new bytes(32);
    for (uint i = 0; i < 32; i++) {
        b[i] = byte(uint8(x / (2**(8*(31 - i))))); 
    }
}

Basado en el chat de Solidity Gitter.

Si está demasiado preocupado por la gasolina, también podemos mejorar la respuesta de @NickJohnson.

function toBytes(uint _num) returns (bytes _ret) {
    assembly {
        _ret := mload(0x10)
        mstore(_ret, 0x20)
        mstore(add(_ret, 0x20), _num)
    }
}

Esto reducirá aproximadamente un 15 % adicional el costo de la gasolina, solo tenga cuidado de no usar esa memoria para otra cosa, ya que 0x10 es una referencia directa a la memoria.

Además, si desea copiar un int de 8 bits a un byte, aquí hay una alternativa que solo usa la mitad del costo del gas:

function toByte(uint8 _num) returns (byte _ret) {
    assembly {
        mstore8(0x20, _num)
        _ret := mload(0x20)
    }
}

Nuevamente, tenga en cuenta que 0x20 es otra referencia directa a la memoria y, con toda honestidad, me quedaría con esta.

function toByte(uint8 _num) returns (byte _ret) {
    return byte(_num);
}

El precio del gas entre los dos era casi idéntico, pero el ASM funcional lo estaba rebajando en alrededor de 70 wei.

Editar: si le preocupa sobrescribir la memoria, puede usar:

let m_alloc := add(msize(),0x1)

En lugar de hacer referencia a la memoria usted mismo

No sabía que msize() se puede usar para aumentar la asignación de memoria al agregarla. El documento de ensamblaje de solidity ( solidity.readthedocs.io/en/v0.4.24/… ) es muy breve. ¿Hay un mejor documento de referencia por ahí?
msize no lo hace, devuelve el punto más alto en la memoria y simplemente indica que desea escribir en msize() más 1. Aquí hay otra página de la pila donde lo explico con más detalle ethereum.stackexchange.com/questions/9537/…

Puede convertir a bytes32 y luego convertir a bytes:

uint u = 200;
bytes32 b = bytes32(u);
bytes memory c = new bytes(32);
for (uint i=0; i < 32; i++) {
    c[i] = b[i];
}

Puede evitar rellenar la matriz de bytes con 0 con las toBytesimplementaciones determinando scriptNumSize

function scriptNumSize(uint256 i) public view returns (uint256) {
    if      (i > 0x7fffffff) { return 5; }
    else if (i > 0x7fffff  ) { return 4; }
    else if (i > 0x7fff    ) { return 3; }
    else if (i > 0x7f      ) { return 2; }
    else if (i > 0x00      ) { return 1; }
    else                     { return 0; }
}

function toBytes(uint256 x) public view returns (bytes memory b) {
    uint a = scriptNumSize(x);
    b = new bytes(a);
    for (uint i = 0; i < a; i++) {
        b[i] = byte(uint8(x / (2**(8*(a - 1 - i))))); 
    }
}

Por ejemplo, uint 1563384765 devolvería 0x5d2f5bbd no 0x00000000000000000000000000000000000000000000000000000000005d2f5bbd

Esto fue escrito específicamente para las marcas de tiempo. Si desea números más grandes, puede agregar más casos.