¿Cómo puedo dividir una fuente Bytes32 en dos mitades y almacenarlas en Bytes16?

Quiero dividir una fuente de 32 bytes: la primera mitad en Bytes16 mitad 1; segunda mitad en Bytes16 mitad2. Mi código funciona, sin embargo, solo para matriz dinámica, no con tamaño fijo 16.

pragma solidity ^0.4.8;

contract cut {

    function cutSha(bytes32 source) constant returns (bytes, bytes) {
        bytes memory half1 = new bytes(16);
        bytes memory half2 = new bytes(16);
        for (uint j = 0; j < 16; j++) {
                half1[j] = source[j];
                half2[j] = source[j+16];
        }
        return (half1, half2);
    }
}
Dado que las mitades son solo los primeros/últimos 16 bytes, debería haber una forma eficiente, como usar ensamblaje en línea y mload.

Respuestas (4)

Es posible hacer esto con ensamblaje:

pragma solidity ^0.4.8;

contract c {
    event trace(bytes32 x, bytes16 a, bytes16 b);

    function foo(bytes32 source) {
        bytes16[2] memory y = [bytes16(0), 0];
        assembly {
            mstore(y, source)
            mstore(add(y, 16), source)
        }
        trace(source, y[0], y[1]);
    }
}

Por ejemplo, convertir bytes de la cadena "¡Qué mundo tan maravilloso!" produce esto después de usar gas 2245:

trace[
  "0x77686174206120776f6e64657266756c20776f726c6421000000000000000000",
  "0x77686174206120776f6e64657266756c",
  "0x20776f726c6421000000000000000000"
]

NB : el código se basa en la representación de datos internos que pueden estar sujetos a cambios en versiones posteriores de Solidity o interferir con el optimizador de Solidity de manera impredecible.

Acabo de ver esto ahora. Muchas gracias, compararé ahora con mi propia solución publicada a continuación.
¿Cómo cambiamos su código para que pueda tomar una entrada de 56 caracteres y dividirlos en dos bytes32?

Encontré una solución usando ensamblaje en línea:

contract cutByte32 {

  //"0xa9c40ddcb43ebbc83add97b8f9f361f12b19bceff2f76b68f66b5bb1812365a9"
  //use this as remix command

  function cut(bytes32 sha) constant returns (bytes16 half1, bytes16 half2) {
    assembly {
      let freemem_pointer := mload(0x40)
      mstore(add(freemem_pointer,0x00), sha)
      half1 := mload(add(freemem_pointer,0x00))
      half2 := mload(add(freemem_pointer,0x10))
    }
  }
}
+1, eso es bueno. pero me puedes explicar un poco que mload(0x40)significa?
Gracias, en la dirección 0x40 (definición de solidez) se almacena el puntero (/dirección) para la siguiente ranura libre en la memoria. Al cargarlo primero en la variable freemem_pointer, me aseguro de no sobrescribir nada en la memoria. Sin embargo, supongo que hay soluciones aún mejores. Si supiera en qué dirección se almacena el valor del parámetro "bytes32 sha", no necesito almacenarlo nuevamente y puedo comenzar a leerlo directamente en 16 bytes.
¿Cómo cambiamos su código para que pueda tomar una entrada de 56 caracteres y dividirlos en dos bytes32?

Ahora es posible usar conversiones de tipo para hacerlo en un par de líneas.

pragma solidity 0.8.16;

contract cut {

    function cutSha(bytes32 source) 
        public 
        returns (bytes16 half1, bytes16 half2) 
    {
        half1 = bytes16(source);
        half2 = bytes16(uint128(uint256(source)));
        
    }
}

PD Lo siento si este código necesitará algo de pelusa, no puedo probarlo ahora, esto es solo una ilustración de la idea; así que cualquier corrección es bienvenida.

Intenta hacerlo con uints

   function bytesChunck(bytes32 source, uint start, uint numBytes) constant returns(uint _result){
                uint counter = 0;
                uint result;

                for(uint i = 0; i < numBytes; i++) {
                    result += uint8(source[start + i]);
                }
                return result;
        }

pero leer bytes de bytes32 devuelve valor cero (excepto el primero)