Verifizieren von signierten Daten

Ich bin damit beschäftigt, mich mit der Wiederherstellung einer Adresse mit ecrecover() zu beschäftigen. Ich habe Probleme, die richtige Adresse wiederherzustellen, wenn ich signierte Daten für den Vertrag bereitstelle.

Der Vertrag, den ich teste, ist unten dargestellt:

pragma solidity ^0.4.24;

contract VerifyTest {
    // https://ethereum.stackexchange.com/a/15911
    function verifyMessage(bytes32 messageHash, uint8 v, bytes32 r, bytes32 s) private view returns (bool) {
        bytes memory hashPrefix = "\x19Ethereum Signed Message:\n32";
        bytes32 prefixedHash = keccak256(abi.encodePacked(hashPrefix, messageHash));
        return ecrecover(prefixedHash, v, r, s) == msg.sender;
    }

    function testBuyOrder(uint256 orderTotal, address tokenContract, uint8 v, bytes32 r, bytes32 s) public view returns (bool) {
        bytes32 messageHash = keccak256(abi.encodePacked(orderTotal, tokenContract));
        return verifyMessage(messageHash, v, r, s);
    }
}

Beim Kompilieren wird das folgende ABI-Objekt generiert:

[
    {
        "constant": true,
        "inputs": [
            {
                "name": "orderTotal",
                "type": "uint256"
            },
            {
                "name": "tokenContract",
                "type": "address"
            },
            {
                "name": "v",
                "type": "uint8"
            },
            {
                "name": "r",
                "type": "bytes32"
            },
            {
                "name": "s",
                "type": "bytes32"
            }
        ],
        "name": "testBuyOrder",
        "outputs": [
            {
                "name": "",
                "type": "bool"
            }
        ],
        "payable": false,
        "stateMutability": "view",
        "type": "function"
    }
]

testBuyOrder()Ich kann die Funktion mit dem folgenden kleinen JavaScript-Snippet erfolgreich aufrufen :

// Trying to keep it concise; assume we have a functioning web3 instance...

const signTestAddr = "[ADDRESS_OF_DEPLOYED_CONTRACT]";
const signTestABI = [...]; // ABI quoted above...

testSignMessage = () => {
    console.log(web3.version.api);

    let tokenAddr = "0x128Df2a07Dc41E034bD9a3CEaddDc0341250a6C8";
    let verifyTest = web3.eth.contract(signTestABI).at(signTestAddr);
    let orderTotal = web3.fromDecimal(100000000);
    console.log(orderTotal);

    let testHash = web3.sha3(orderTotal + tokenAddr);

    signHashedMessage(testHash, (error, signature) => {
        if(error === null) {
            signature = signature.substring(2);
            let r = '0x' + signature.substring(0, 64);
            let s = '0x' + signature.substring(64, 128);
            let v = '0x' + signature.slice(128, 130);
            let vDec = parseInt(v, 16);

            // Call the function on our contract here...
            verifyTest.testBuyOrder(100000000, tokenAddr, vDec, r, s, (testErr, result) => {
                    if(testErr === null) {
                        console.log("Success: " + result);
                    } else {
                        console.log("Error: " + testErr);
                    }
            });

        } else {
            console.log("error: " + error);
        }
    })
};

Die Ausgabe des obigen Javascripts lautet:

0.20.3
0x5f5e100
Success: false

Der obige Code ruft den Vertrag erfolgreich auf, aber ich kann nicht sehen, warum der Aufruf testBuyOrder() immer falsch zurückgibt.

Edit0: Außerdem entschuldige ich mich für die scheinbar doppelte Frage, aber ich habe vergeblich versucht, die Weisheit der Antworten anderer anzuwenden.


Bearbeiten1:

Ismaels erster Vorschlag war, Hash-Funktionen in den Vertrag selbst aufzunehmen, die sicherstellen würden, dass die Daten korrekt verkettet und formatiert werden, bevor sie gehasht werden. Die Funktion von Ismael musste leicht modifiziert werden, um den korrekten zu signierenden Hash zu erhalten.

Die Arbeitslösung für diese Funktion ist unten:

function getMessageHash(uint256 orderTotal, address tokenContract) public pure returns (bytes32) {
    bytes memory hashPrefix = "\x19Ethereum Signed Message:\n32";
    bytes32 messageHash = keccak256(abi.encodePacked(orderTotal, tokenContract));
    return keccak256(abi.encodePacked(hashPrefix, messageHash));
}

Edit2:

Nachdem ich die Verwendung von Web3 v1.0 untersucht habe, kämpfe ich immer noch damit, die Absenderadresse im Vertrag wiederherzustellen.

Mein aktualisierter Client-Code sieht wie folgt aus:

signHashedMessage = (messageHash, callback) => {
    web3.eth.getCoinbase().then((coinbase) => {
        web3.eth.sign(prefixHashedData(messageHash), coinbase, callback);
    });
};

prefixHashedData = (messageHash) => {
    let msgPrefix = "\x19Ethereum Signed Message:\n32";
    return web3.utils.soliditySha3(msgPrefix, messageHash);
};

testSignMessage = () => {

    let verifyTest = new web3.eth.Contract(signTestABI, signTestAddr);

    let tokenAddr = "0x128Df2a07Dc41E034bD9a3CEaddDc0341250a6C8";
    let orderTotal = 100000000;

    let testHash = web3.utils.soliditySha3(orderTotal, tokenAddr);

    signHashedMessage(testHash, (error, signature) => {
        if(error === null) {
            signature = signature.substring(2);
            let r = '0x' + signature.substring(0, 64);
            let s = '0x' + signature.substring(64, 128);
            let v = '0x' + signature.slice(128, 130);
            let vDec = parseInt(v, 16);

            // Call the function on our contract here...
            let testBuyOrderRes = verifyTest.methods.testBuyOrder(orderTotal, tokenAddr, vDec, r, s);
            testBuyOrderRes.call((callError, callResult) => {
                if (callError === null) {
                    console.log("Verified: " + callResult);
                } else {
                    console.log("Error: " + callError);
                }
            });
        } else {
            console.log("error: " + error);
        }
    });
};

Ich habe signHashedMessage()und prefixHashedMessage()der Vollständigkeit halber hinzugefügt, um meine Arbeit zu zeigen.

Um dies zu überprüfen prefixHashedData(), habe ich getMessageHash()die oben beschriebene zu meinem Vertrag hinzugefügt. Beim Aufrufen getMessageHash()wurde derselbe Hash zurückgegeben, der beim Verwenden zurückgegeben wurde, web.utils.soliditySha3()wie Ismael es beschrieben hatte.

Das neue Problem, das ich mit dem obigen Code von Web3 v1.0 habe; Wenn Sie die Signatur des Vertrags überprüfen, indem Sie aufrufen testBuyOrder(), gibt die Funktion zurück false.

Kann jemand den Fehler sehen, den ich mache?

Antworten (1)

Die Nachricht, die Sie an den Vertrag senden, ist nicht die gleiche, die Sie in Javascript kodieren, orderTotal + tokenAddrist nicht die gleiche wie abi.encodePacked(orderTotal, tokenContract).

Verwenden Sie entweder die gleiche Funktion, die Sie im Vertrag verwenden.

function getMessageHash(uint256 orderTotal, address tokenContract) public pure returns (bytes32) {
    bytes32 messageHash = keccak256(abi.encodePacked(orderTotal, tokenContract));
    returnmessageHash;
}

Und nennen Sie das von Javascript verifyTest.getMessageHash(orderTotal, tokenAddr).

Eine andere Möglichkeit besteht darin, soliditysha3 von web3 v1.0 aufzurufen , das die Daten richtig formatiert.

Vielen Dank für Ihre Hilfe! Ich habe es geschafft, gute Ergebnisse mit Ihrem ersten Vorschlag zu erzielen. Ihre Antwort ist fast da – ich musste das Ethereum Signed Message- Präfix voranstellen, bevor ich den Hash zurückgab, aber diese Signatur führt zu einer guten Adresswiederherstellung. Diese Methode erscheint schnell auf einem lokalen Testnetz, aber wäre ich richtig in der Annahme, dass es beim Berechnen des Hashs mehr Latenz geben würde, wenn es um Haupt-/Rinkeby-/Kovan-Netze geht?
Wenn Sie Ihren Vertrag anrufen, wird die Latenz erhöht. Wie lange die Verzögerung sein wird, hängt von der Konfiguration Ihres Knotens ab, wenn Sie den infura+vm-Anbieter verwenden, ist sie minimal (sobald der Vertrags-Bytecode zwischengespeichert ist, erfolgt die Ausführung lokal), aber sie ist immer noch höher als beim Aufruf von soliditySha3().
Danke für die Klarstellung. Ich werde die web3 v1.0-API studieren und versuchen, eine lokale Datenverbindung zum Laufen zu bringen. Ich werde meine Frage aktualisieren, um die Lösungen zu zeigen, zu denen Sie mich geführt haben. Danke nochmal für deine Hilfe.