Ich weiß, dass diese Frage gestellt und beantwortet wurde, aber beim Versuch, alles zum Laufen zu bringen, bin ich auf einige Probleme gestoßen. Dies ist, was ich versuche zu tun:
1. take a target string 'Schoolbus'
2. use JSON with geth to eth_sign it
3. obtain v,r,s of signature
4. attempt to verify with a solidity contract, need the hash of 'Schoolbus'
Also hier ist, was ich habe. Erstens können wir nicht alle denselben privaten Schlüssel verwenden. Wenn also jemand meine Arbeit überprüfen und einen Überblick über mein Problem bekommen könnte, wäre das großartig.
Ich gebe vor, mein privater Schlüssel sei „0xd1ade25ccd3d550a7eb532ac759cac7be09c2719“, um „Schoolbus“ zu signieren
curl -X POST --data '{"jsonrpc":"2.0","method":"eth_sign","params":["0xd1ade25ccd3d550a7eb532ac759cac7be09c2719", "Schoolbus"],"id":1}'
Wo bekomme ich das Ergebnis
0x2ac19db245478a06032e69cdbd2b54e648b78431d0a47bd1fbab18f79f820ba407466e37adbe9e84541cab97ab7d290f4a64a5825c876d22109f3bf813254e8601
Ich verwende (ich könnte mich hier irren)
v=2a
r=c19db245478a06032e69cdbd2b54e648b78431d0a47bd1fbab18f79f820ba407
s=466e37adbe9e84541cab97ab7d290f4a64a5825c876d22109f3bf813254e8601
Dann habe ich meinen Vertrag auf die Beine gestellt, der eine Variation einer Antwort in einem verwandten Thread ist:
contract Auth {
function verify( bytes32 hash, uint8 v, bytes32 r, bytes32 s) constant returns(address retAddr) {
retAddr= ecrecover(hash, v, r, s);
}
}
Da ich nie den Hash von 'Schoolbus' bekommen habe, habe ich ein paar Dinge in web3.js ausprobiert (der UTF8 hat mich verwirrt, ich hatte erwartet, dass die {encoding:'hex'}-Version die richtige ist):
console.log('1 '+ web3.sha3(web3.toHex('Schoolbus'))); //05ab39621b81764697fcfb6ae4fcf6b023cd644721c67c13a49fbd769c75671c
console.log('2 '+ web3.sha3(web3.toHex('Schoolbus'),{encoding:'hex'}));//d030d9a04df643f62a1502b017f51c41a659268091abbd20e2de97b935724d7c
console.log('3 '+ web3.sha3('Schoolbus'));//d030d9a04df643f62a1502b017f51c41a659268091abbd20e2de97b935724d7c
console.log('4 '+ web3.sha3( unescape(encodeURIComponent('Schoolbus')) ) ); //to UTF8 //d030d9a04df643f62a1502b017f51c41a659268091abbd20e2de97b935724d7c
console.log('5 '+ web3.sha3( unescape(encodeURIComponent('Schoolbus')), {encoding:'hex'} ) ); //to UTF8 //8f1cbe7efcf383ffeb1aeaf1e826c778a087153344cbeba144fbe967ad3ab11a
Am Ende habe ich das verwendet, weiß aber nicht warum:
0xd030d9a04df643f62a1502b017f51c41a659268091abbd20e2de97b935724d7c
Dann habe ich den Vertrag aufgerufen:
var contDep=web3.eth.contract( [abi def] ).at( contractAddress);
console.log(
contDep.verify('d030d9a04df643f62a1502b017f51c41a659268091abbd20e2de97b935724d7c', 2a,'c19db245478a06032e69cdbd2b54e648b78431d0a47bd1fbab18f79f820ba407', '466e37adbe9e84541cab97ab7d290f4a64a5825c876d22109f3bf813254e8601')
);
Hier ist mein Problem. Ich bekomme immer diese seltsame Adresse zurück. Es beginnt mit 0x, es sind 20 Bytes, aber es ist kein [af] drin:
0x3433663632613135303262303137663531633431
Wenn ich r und s vertausche, bekomme ich fast das gleiche Ergebnis zurück.
Ich habe mich gefragt, ob jemand meine Erfahrung bestätigen oder darauf hinweisen kann, was ich falsch gemacht habe. Ich fühle mich hier wie ein Verrückter.
Danke für deine Hilfe.
Ich hatte früher das gleiche Problem, also werde ich eine ausführliche Antwort darauf geben, wie das funktioniert. Ich nehme an, Sie verwenden Geth als Client. Es gibt ein offenes Problem, bei dem der Geth-Client im falschen Format zurückkehrt. Denken wir also daran, dass wir etwas hinzufügen sollten v
, wenn wir ein erhalten . Wenn Sie node ausführen und web3 mit Ihrem bevorzugten Client verbunden haben:v
0
1
27
var msg = web3.sha3('Schoolbus')
var signature = web3.eth.sign(web3.eth.accounts[0], msg)
In meinem Fall lautet die Signatur:
0x28c412923e03982efdff078f78bb70eaefe32c11751b0c23858191c18dddc4ba72c3667c07672b97c022beb857afb99c49b7084da1608e20392c274adc7dd5851c
Die Zeichenfolge repräsentiert r
, s
, v
bzw. in dieser Reihenfolge . Um es jedoch in Ihren Auth-Vertrag einzuspeisen, müssen Sie es in v
ein konvertieren uint8
und hinzufügen, um sicherzustellen, dass das Hex-Präfix 0x
überall vorhanden ist:
var r = signature.slice(0, 66)
var s = '0x' + signature.slice(66, 130)
var v = '0x' + signature.slice(130, 132)
v = web3.toDecimal(v)
msg = '0x' + msg
Denken Sie daran, das v
sollte 27
oder sein 28
! Ist dies nicht der Fall, setzen Sie v = v + 27
. Sie können Ihre Überprüfungsfunktion jetzt wie folgt aufrufen:
var addr = Auth_instance.verify.call(msg, v, r, s)
und Sie können überprüfen, ob addr
das denselben Wert hat wie web3.eth.accounts[0]
.
Hier ist ein funktionierendes Beispiel, das ich mit Trüffel getestet habe:
Beispiel.sol
pragma solidity ^0.4.0;
contract Example {
function testRecovery(bytes32 h, uint8 v, bytes32 r, bytes32 s) returns (address) {
/* prefix might be needed for geth only
* https://github.com/ethereum/go-ethereum/issues/3731
*/
// bytes memory prefix = "\x19Ethereum Signed Message:\n32";
// h = sha3(prefix, h);
address addr = ecrecover(h, v, r, s);
return addr;
}
}
Hier sind einige Beispiele, die zeigen, wie Sie die v-, r- und s-Werte mithilfe von Slicing erhalten und testen, ob ecrecover die Adresse zurückgibt, die die Nachricht signiert hat:
var Example = artifacts.require('./Example.sol')
var Web3 = require('web3')
var web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8545'))
contract('Example', (accounts) => {
var address = accounts[0]
it('ecrecover result matches address', async function() {
var instance = await Example.deployed()
var msg = '0x8CbaC5e4d803bE2A3A5cd3DbE7174504c6DD0c1C'
var h = web3.sha3(msg)
var sig = web3.eth.sign(address, h).slice(2)
var r = `0x${sig.slice(0, 64)}`
var s = `0x${sig.slice(64, 128)}`
var v = web3.toDecimal(sig.slice(128, 130)) + 27
var result = await instance.testRecovery.call(h, v, r, s)
assert.equal(result, address)
})
})
Lauftest:
$ truffle test
Using network 'development'.
Compiling ./contracts/Example.sol...
Contract: Example
✓ ecrecover result matches address (132ms)
1 passing (147ms)
Es ist wahrscheinlich besser, die Präfixierung auf Anwendungsebene statt im Soliditätsvertrag vorzunehmen, da dies billiger ist.
Hier ist eine Hilfsbibliothek mit einer Methode, die einen Hash der Daten und der Signatur akzeptiert und die Signaturadresse zurückgibt. Der Smart Contract übernimmt das Abrufen der v-, r- und s-Werte, anstatt dies auf Anwendungsebene zu tun:
ECVerify.sol
pragma solidity ^0.4.4;
/*
* @credit https://gist.github.com/axic/5b33912c6f61ae6fd96d6c4a47afde6d
*/
library ECVerify {
function ecrecovery(bytes32 hash, bytes sig) public returns (address) {
bytes32 r;
bytes32 s;
uint8 v;
if (sig.length != 65) {
return 0;
}
assembly {
r := mload(add(sig, 32))
s := mload(add(sig, 64))
v := and(mload(add(sig, 65)), 255)
}
// https://github.com/ethereum/go-ethereum/issues/2053
if (v < 27) {
v += 27;
}
if (v != 27 && v != 28) {
return 0;
}
/* prefix might be needed for geth only
* https://github.com/ethereum/go-ethereum/issues/3731
*/
// bytes memory prefix = "\x19Ethereum Signed Message:\n32";
// hash = sha3(prefix, hash);
return ecrecover(hash, v, r, s);
}
function ecverify(bytes32 hash, bytes sig, address signer) public returns (bool) {
return signer == ecrecovery(hash, sig);
}
}
Hier sind einige Beispiele zum Erstellen und Testen von Signaturen mit web3:
var ECVerify = artifacts.require('./ECVerify.sol')
contract('ECVerify', (accounts) => {
it('should return signing address from signature', async () => {
var account = accounts[0]
try {
var instance = await ECVerify.deployed()
var msg = 'some data'
var hash = web3.sha3(msg)
var sig = web3.eth.sign(account, hash)
var signer = await instance.ecrecovery(hash, sig)
assert.ok(signer)
} catch(error) {
console.error(error)
assert.equal(error, undefined)
}
})
it('should verify signature is from address', async () => {
var account = accounts[0]
try {
var instance = await ECVerify.deployed()
var msg = 'some data'
var hash = web3.sha3(msg)
var sig = web3.eth.sign(account, hash)
var verified = await instance.ecverify.call(hash, sig, account)
assert.ok(verified)
} catch(error) {
console.error(error)
assert.equal(error, undefined)
}
})
})
Testen, ob es funktioniert:
$ truffle test
Compiling ./contracts/ECVerify.sol...
Compiling ./contracts/Migrations.sol...
Contract: ECVerify
✓ should return signing address from signature (182ms)
✓ should verify signature is from address (142ms)
2 passing (342ms)
Verwandt
Miguel Motas Antwort deckt die hervorstechenden Details ab. Die folgende Zeile -> hat jedoch var sig = web3.eth.sign(account, hash)
einen Fehler ausgelöst. Error: Provided address is invalid, the capitalization checksum test failed, or its an indrect IBAN address which can't be converted.
Anscheinend scheinen diese Parameter in Versionen von web3.js umgekehrt zu sein, und Sie müssen msg
als ersten Parameter und account/address
als zweiten Parameter angeben. Hier ist ein Beispieltest, der bei mir funktioniert hat.
var address = accounts[0];
it('ecrecover result matches address', async function() {
var instance = await Adoption.deployed()
let msg = 'I really did make this message';
let prefix = "\x19Ethereum Signed Message:\n" + msg.length
let h = web3.utils.sha3(prefix+msg)
console.log(`sha3 hash ${h}`);
let sig1 = await web3.eth.sign(msg, address);
console.log(`signature: ${sig1}`)
var sig = sig1.slice(2)
var r = `0x${sig.slice(0, 64)}`
var s = `0x${sig.slice(64, 128)}`
var v = web3.utils.toDecimal(sig.slice(128, 130)) + 27
var result = await instance.recoverAddr.call(h, v, r, s)
console.log(`address: ${address}, result ${result}`)
assert.equal(result, address)
})
Mein entsprechender Vertragscode lautete:
function recoverAddr(bytes32 msgHash, uint8 v, bytes32 r, bytes32 s) public view returns (address) {
return ecrecover(msgHash, v, r, s);
}
Für Geth-Benutzer funktioniert Folgendes. Habe es vor kurzem getestet. Erstens stammt der Testvertragscode aus dem Kommentar der obersten Ebene. Sie können diesen Code unter https://remix.ethereum.org einfügen
pragma solidity ^0.4.0;
contract Example {
function testRecovery(bytes32 h, uint8 v, bytes32 r, bytes32 s) returns (address) {
/* prefix might be needed for geth only
* https://github.com/ethereum/go-ethereum/issues/3731
*/
// bytes memory prefix = "\x19Ethereum Signed Message:\n32";
// h = sha3(prefix, h);
address addr = ecrecover(h, v, r, s);
return addr;
}
}
Laden Sie auf einer beliebigen Webseite die Datei web3.js. Fügen Sie dann in der Entwicklertools-Konsole diese Funktionen einfach ein. Rufen Sie die Funktion verificationScheme(msg)
mit der msg im ASCII-Klartext auf.
function tohex(msg){
var hexmsg = "";
for(var i=0; i<msg.length; i++){
hexmsg += msg.charCodeAt(i).toString(16);
}
return "0x"+hexmsg;
}
function verificationScheme(str){
var msghex = tohex(str);
var sig = web3.eth.sign(web3.eth.accounts[0], msghex);
var r = sig.slice(0, 66);
var s = '0x' + sig.slice(66, 130);
var v = '0x' + sig.slice(130, 132);
v = web3.toDecimal(v);
var verificationMessage = "\x19Ethereum Signed Message:\n" + str.length + str;
var verificationMessageHash = web3.sha3(verificationMessage);
return [verificationMessageHash, v, r, s];
}
Also, meine Ergebnisse waren diese: eth.accounts[0]
im Funktionscode zeigt auf "0xebbc50c7afc14c693bfc26868c490ce0819cef4f"
. Im Grunde wurde also diese Adresse zum Unterzeichnen verwendet, und das sollte die endgültige Ausgabe des Vertrags sein. Ich habe angerufen verificationScheme("hello")
.
Ich habe dieses Array zurück:["0x50b2c43fd39106bafbba0da34fc430e1f91e3c96ea2acee2bc34119f92b37750", 27, "0x43653d23758f13a45c498fc96c8d5d07e9fc24123d967b0cb29c48cd48e4c907", "0x365d7cebd54f5f5298b740b44004f9808a2c9791a1c2d5a7137602a0a2742f28"]
Dann habe ich die testRecovery-Funktion des Beispielvertrags mit allen Argumenten in diesem Array in dieser Reihenfolge aufgerufen. Ich habe meine ursprüngliche Adresse "0xebbc50c7afc14c693bfc26868c490ce0819cef4f"
als Ausgabe zurückbekommen.
schiso
MrChico
schiso
Ajoy Bhatia
web3.eth.sign(...)
die Adresse des Kontos, mit dessen privatem Schlüssel Sie signieren möchten. Der Wert, den Sie senden, also 40 Hex-Zeichen (dh 20 Byte), ist die richtige Länge für eine Adresse. Aus Ihrem Kommentar geht hervor, dass Sie die Adresse wirklich senden . Nennen Sie es einfach nicht einen "privaten Schlüssel". Eine Adresse ist in hohem Maße eine öffentliche Information. (Er wird normalerweise auf irgendeine Weise aus dem öffentlichen Schlüssel generiert.Justin Thomas
willjgriff