Wie konvertiert man in Solidity ein uint in Bytes?

uintGibt es eine einfache Möglichkeit, a in Solidity umzuwandeln bytes?

Antworten (7)

Es scheint jetzt so zu sein, seit der Solidity-Version 0.4.24 können Sie abi.encodePacked verwenden

Z.B:

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

Die Alternative zur Antwort von @eth ist die Verwendung von Assembly:

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

Dies ist deutlich sparsamer, hängt jedoch vom internen Speicherlayout ab, das vom Solidity-Compiler verwendet wird. Theoretisch kann sich das in Zukunft ändern, aber in der Praxis sollte es ziemlich stabil sein.

Würden Sie erklären, was jede Zeile tut? Was ist der Zweck dieses Zusatzes (b, 32). Könnten wir nicht einfach mstore(b, x) machen?
@ppoliani hast du eine Antwort auf deine Frage bekommen? Ich bin auch neugierig, warum wir 32 zu b hinzufügen. (wobei b sowieso am Anfang der Speicheradresse stehen sollte).
Eigentlich wird es hier erklärt: ethereum.stackexchange.com/questions/98750/…
@ppoliani Der bytesTyp in Solidity wird im Speicher gespeichert als: 1) zuerst 32 Bytes = Länge des bytesWerts, 2) dann der bytesWert selbst. Der mstoreOpcode speichert 32 Bytes, beginnend mit einem bestimmten Offset (in dem zu erfassenden Bytes-Wert) bis zu einer bestimmten Stelle im Speicher. Die Anweisung mstore(add(b, 32), x)kann also in einfachen Worten übersetzt werden als "speichert 32 Bytes an Position x im Speicher, beginnend mit dem 32. Byte-Offset im b" (um also die Länge zu überspringen und sich nur bselbst zu speichern).

Hier ist ein Vergleich des Gases, das in den drei Methoden von @NickJohnson, @Eth und @k26dr verwendet wird. Ich habe constantden Funktionsmodifikatoren ein hinzugefügt, da diese Funktionen die Blockchain nicht verändern:

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];
        }
    }    
}

Sie können die Gaskosten für die Ausführung dieser Funktionen in Remix (Solidity Browser) sehen:

Geben Sie hier die Bildbeschreibung ein

Es gibt keine einfachen Möglichkeiten, etwas in bytes. Hier ist eine Funktion:

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))))); 
    }
}

Basierend auf dem Chat von Solidity Gitter.

Wenn Sie sich übermäßig Sorgen um Benzin machen, können wir auch die Antwort von @NickJohnson verbessern.

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

Dadurch werden die Benzinkosten um weitere 15 % gesenkt. Achten Sie jedoch darauf, dass Sie diesen Speicher nicht für etwas anderes verwenden, da 0x10 ein direkter Verweis auf den Speicher ist.

Wenn Sie ein 8-Bit-Int in Byte kopieren möchten, finden Sie hier eine Alternative, die nur die Hälfte der Gaskosten verbraucht:

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

Denken Sie auch hier daran, dass 0x20 eine weitere direkte Speicherreferenz ist, und ehrlich gesagt würde ich bei dieser bleiben

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

Der Gaspreis zwischen den beiden war fast identisch, aber das funktionierende ASM unterbot ihn um etwa 70 Wei

Bearbeiten: Wenn Sie Bedenken haben, den Speicher zu überschreiben, können Sie Folgendes verwenden:

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

Anstatt sich selbst auf die Erinnerung zu beziehen

Ich wusste nicht, dass msize() verwendet werden kann, um die Speicherzuweisung durch Hinzufügen zu erhöhen. Das Solidity Assembly-Dokument ( solidity.readthedocs.io/en/v0.4.24/… ) ist sehr kurz. Gibt es ein besseres Referenzdokument?
msize tut dies nicht, es gibt den höchsten Punkt im Speicher zurück und Sie geben einfach an, dass Sie in msize() plus 1 schreiben möchten. Hier ist eine weitere Stack-Seite, auf der ich es ausführlicher erkläre ethereum.stackexchange.com/questions/9537/…

Sie können in Bytes32 konvertieren und dann in Bytes konvertieren:

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

Sie können das Auffüllen des Bytes-Arrays mit Nullen bei den toBytesImplementierungen vermeiden, indem Sie die scriptNumSize bestimmen

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))))); 
    }
}

Zum Beispiel würde uint 1563384765 0x5d2f5bbd zurückgeben, nicht 0x0000000000000000000000000000000000000000000000000000005d2f5bbd

Dies wurde speziell für Zeitstempel geschrieben. Wenn Sie größere Zahlen wünschen, können Sie weitere if-Fälle hinzufügen.