The Unicorn Mystery - Solidity kann gültige UTF-8-codierte Bytes nicht decodieren

Die Ethereum Foundation hat das Einhorn-Token, um Spenden zu fördern, und das Einhorn-Emoji wird durch drei Bytes dargestellt. Wie um alles in der Welt haben sie das gemacht?

Ich weiß, dass Solidity Unicode-Escapes unterstützt, also wird etwas wie \u2934in einer Zeichenfolge in Mist als ⤴ angezeigt. Aber was ist mit so etwas wie dem Welpen-Emoji 🐶?

Man könnte meinen, die Escape-Sequenz wäre \u1F436, aber stattdessen zeigt Mist nur ein Zeichen, das nicht das ist, was ich will, vermutlich welches Emoji auch immer die Codierung hat \u1F43.

Also habe ich es dann mit zwei Unicode-Punkten versucht: \uD83D\uDC36. Mist zeigte nichts.

Das folgende Commit lässt mich denken, dass dies unmöglich ist, weil es mir so vorkommt, als hätte for loopes ivier Zeichen oder zwei Bytes für jede Escape-Sequenz durchlaufen:
https://github.com/ethereum/solidity/pull/666/commits/aa4593cab3d60468e5ea4318012c5252ebbc7d13

Und wie bereits erwähnt, scheinen Unicode-Punkte nicht zu funktionieren oder werden zumindest nicht in Mist angezeigt (noch zeigt Mist das Einhorn-Emoji).

Wie um alles in der Welt unterstütze ich ein Emoji, das aus 3+ Bytes besteht?

BEARBEITEN:

0xcafffand einen Fehler in der Art und Weise, wie Solidity UTF-8-codierte Bytes dekodiert.

Ich habe das Problem auf GitHub gemeldet: https://github.com/ethereum/solidity/issues/2383
Wenn jemand eine Lösung hat, gehört das Kopfgeld ihm. Sonst geht es an 0xcaff.

BEARBEITEN II:

Das Problem wurde geschlossen, die UTF-8-Validierung wurde behoben (oder zumindest verbessert) und Änderungen wurden zusammengeführt: https://github.com/ethereum/solidity/pull/2386

Jetzt können Sie Emojis in Solidity mit etwas wie:

string public constant working = hex"F09F90B6";

Verwenden der UTF-8-codierten Bytes, die von Websites wie https://mothereff.in/utf-8 generiert werden können .

Woohoo! 🌈🦄🐶

Oh, und die Unicorn-Dokumentation ist auch nicht sehr hilfreich: github.com/bokkypoobah/TokenTrader/wiki/…
Wo genau siehst du die Einhorn-Symbole? Ich habe sie bisher nur als eingebettete .pngDateien gesehen, aber vielleicht haben Sie sie woanders gesehen :)
Ok, tut mir leid, du sagst in Mist...
Was in diesem Commit hinzugefügt wurde: github.com/ethereum/ethereum-org/commit/…
Hmm, ich dachte, es könnte das sein ...
... aber das bedeutet ein anderes Symbol. (URL ist zu lang, um sie hier einzufügen, aber kopieren Sie sie in die URL-Leiste und sie wird gerendert.) Interessante Frage. +1
Update : Zusätzlich zu den oben genannten Updates unterstützt Solidity heutzutage entweder das Einfügen von Unicode in eine ASCII-Zeichenfolge (Standard), wobei verwendet wird, \uNNNNwo NNNNder Code des Zeichens ist, oder eine Unicode-Zeichenfolge, die durch angegeben wird unicode"", Quelle: docs.soliditylang.org/en/v0 .8.0/…

Antworten (1)

Im Gegensatz zu einigen Kommentaren befindet sich das Einhorn-Symbol 🦄 ( U+1F984) im Symbolnamen des Vertrags. Sie können dies überprüfen, indem Sie Folgendes in der Konsole eines Web3-Browsers ausführen:

// taken from https://github.com/flyswatter/human-standard-token-abi/blob/master/index.js
var tokenAbi = [
  {
    "constant": true,
    "inputs": [],
    "name": "name",
    "outputs": [
      {
        "name": "",
        "type": "string"
      }
    ],
    "payable": false,
    "type": "function"
  },
  {
    "constant": false,
    "inputs": [
      {
        "name": "_spender",
        "type": "address"
      },
      {
        "name": "_value",
        "type": "uint256"
      }
    ],
    "name": "approve",
    "outputs": [
      {
        "name": "success",
        "type": "bool"
      }
    ],
    "payable": false,
    "type": "function"
  },
  {
    "constant": true,
    "inputs": [],
    "name": "totalSupply",
    "outputs": [
      {
        "name": "",
        "type": "uint256"
      }
    ],
    "payable": false,
    "type": "function"
  },
  {
    "constant": false,
    "inputs": [
      {
        "name": "_from",
        "type": "address"
      },
      {
        "name": "_to",
        "type": "address"
      },
      {
        "name": "_value",
        "type": "uint256"
      }
    ],
    "name": "transferFrom",
    "outputs": [
      {
        "name": "success",
        "type": "bool"
      }
    ],
    "payable": false,
    "type": "function"
  },
  {
    "constant": true,
    "inputs": [],
    "name": "decimals",
    "outputs": [
      {
        "name": "",
        "type": "uint8"
      }
    ],
    "payable": false,
    "type": "function"
  },
  {
    "constant": true,
    "inputs": [],
    "name": "version",
    "outputs": [
      {
        "name": "",
        "type": "string"
      }
    ],
    "payable": false,
    "type": "function"
  },
  {
    "constant": true,
    "inputs": [
      {
        "name": "_owner",
        "type": "address"
      }
    ],
    "name": "balanceOf",
    "outputs": [
      {
        "name": "balance",
        "type": "uint256"
      }
    ],
    "payable": false,
    "type": "function"
  },
  {
    "constant": true,
    "inputs": [],
    "name": "symbol",
    "outputs": [
      {
        "name": "",
        "type": "string"
      }
    ],
    "payable": false,
    "type": "function"
  },
  {
    "constant": false,
    "inputs": [
      {
        "name": "_to",
        "type": "address"
      },
      {
        "name": "_value",
        "type": "uint256"
      }
    ],
    "name": "transfer",
    "outputs": [
      {
        "name": "success",
        "type": "bool"
      }
    ],
    "payable": false,
    "type": "function"
  },
  {
    "constant": false,
    "inputs": [
      {
        "name": "_spender",
        "type": "address"
      },
      {
        "name": "_value",
        "type": "uint256"
      },
      {
        "name": "_extraData",
        "type": "bytes"
      }
    ],
    "name": "approveAndCall",
    "outputs": [
      {
        "name": "success",
        "type": "bool"
      }
    ],
    "payable": false,
    "type": "function"
  },
  {
    "constant": true,
    "inputs": [
      {
        "name": "_owner",
        "type": "address"
      },
      {
        "name": "_spender",
        "type": "address"
      }
    ],
    "name": "allowance",
    "outputs": [
      {
        "name": "remaining",
        "type": "uint256"
      }
    ],
    "payable": false,
    "type": "function"
  },
  {
    "inputs": [
      {
        "name": "_initialAmount",
        "type": "uint256"
      },
      {
        "name": "_tokenName",
        "type": "string"
      },
      {
        "name": "_decimalUnits",
        "type": "uint8"
      },
      {
        "name": "_tokenSymbol",
        "type": "string"
      }
    ],
    "type": "constructor"
  },
  {
    "payable": false,
    "type": "fallback"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "name": "_from",
        "type": "address"
      },
      {
        "indexed": true,
        "name": "_to",
        "type": "address"
      },
      {
        "indexed": false,
        "name": "_value",
        "type": "uint256"
      }
    ],
    "name": "Transfer",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "name": "_owner",
        "type": "address"
      },
      {
        "indexed": true,
        "name": "_spender",
        "type": "address"
      },
      {
        "indexed": false,
        "name": "_value",
        "type": "uint256"
      }
    ],
    "name": "Approval",
    "type": "event"
  },
];

// get symbol
var tokenContract = web3.eth.contract(tokenAbi);
var instance = tokenContract.at("0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7");
instance.symbol.call((thing1, thing2) => global.symbol = thing2);

Lassen Sie uns diese Zeichenfolge untersuchen:

symbol.length // Output: 2
symbol.codePointAt(0) // Output: 129412 (0x1F984)
symbol.split("").map(char => char.charCodeAt(0)); // Output: [55358, 56708]

Lassen Sie uns über UTF-8 sprechen, die von Mist verwendete Zeichenkodierung. Laut FileFormat.info :

UTF-8 ist eine Kompromisszeichencodierung, die so kompakt wie ASCII sein kann (wenn es sich bei der Datei nur um einfachen englischen Text handelt), aber auch beliebige Unicode-Zeichen enthalten kann (mit einer gewissen Erhöhung der Dateigröße).

UTF steht für Unicode Transformation Format. Die „8“ bedeutet, dass 8-Bit-Blöcke verwendet werden, um ein Zeichen darzustellen. Die Anzahl der Blöcke, die zur Darstellung eines Zeichens benötigt werden, variiert zwischen 1 und 4.

Was wir hier haben, ist der Unicode-Codepunkt: 29412 (0x1F984). Es sieht so aus, als ob Solidity nur die Codierung von Codepoints zwischen 0x0000 (0) und FFFF (65535) mit der \uNNNNSyntax unterstützt. Typischerweise erlauben Sprachen die Codierung mit mehr als dieser Menge unter Verwendung von Ersatzzeichen. Solidity scheint das nicht zu tun.

Keine Sorge, wir sollten in der Lage sein, einfach die Hex-Kodierung der korrekten Unicode-Sequenz einzugeben, und der korrekte Text sollte gerendert werden.

Vertrag:

pragma solidity ^0.4.11;

contract EmojiCon {
    string public constant working = hex"F09FA684";
    string public constant broken = "\uD83D\uDC36";
}

Test (Trüffel):

var EmojiCon = artifacts.require("./EmojiCon.sol");

contract('EmojiCon', function(accounts) {
  it("should match the string", async function() {
    const instance = await EmojiCon.deployed();
    const workingString = await instance.working.call();
    console.log(workingString);
  });

  it("shouldn't match the string", async function() {
    const instance = await EmojiCon.deployed();
    const brokenString = await instance.broken.call();
    console.log(brokenString);
  });
});

Leider lässt es sich nicht kompilieren:

Compiling ./contracts/EmojiCon.sol...
Compiling ./contracts/Migrations.sol...

/home/vm/ethereum/unicode/contracts/EmojiCon.sol:4:35: : Type literal_string (contains invalid UTF-8 sequence at position 3) is not implicitly convertible to
expected type string memory.
        string public constant working = hex"F09FA68421";
                                         ^-------------^
Compiliation failed. See above.

Der Fehler kommt von hier . Anscheinend wählt der Validator einen falschen Wert für countund stoppt zu früh. ( Isolation ( Original )). Dies scheint ein Fehler / fehlendes Feature in Solidity zu sein. Hoffe das hilft.

Ausgezeichnete Arbeit! Ich reiche ein neues Problem mit Solidity auf GitHub ein. Das Kopfgeld bleibt zu gewinnen, wenn jemand eine tatsächliche Lösung anbieten kann, aber ansonsten werde ich es Ihnen geben, weil Sie das gefunden haben, was meiner Meinung nach die Grundursache ist.
In Ordnung, ich habe ein Problem auf GitHub gemeldet: github.com/ethereum/solidity/issues/2383 . Jeder, der tatsächlich einen Fix veröffentlichen kann, bekommt das Kopfgeld, ansonsten geht es an 0xcaff.