NOTE: The PR slightly changed, so this data might be slightly out of date (but overall, it's still indicative)
$ GOMAXPROCS=1 go test -cpu 1 -bench=BenchmarkPrecompiledEd25519Verify -benchmem
goos: linux
goarch: amd64
pkg: github.com/ethereum/go-ethereum/core/vm
BenchmarkPrecompiledEd25519Verify/valid_input-Gas=1584 17599 67400 ns/op 1656 B/op 17 allocs/op
BenchmarkPrecompiledEd25519Verify/valid_input-Gas=1608 17914 67768 ns/op 1656 B/op 17 allocs/op
BenchmarkPrecompiledEd25519Verify/valid_input_4_word_msg-Gas=1632 17775 68614 ns/op 1656 B/op 17 allocs/op
BenchmarkPrecompiledEd25519Verify/valid_input_with_empty_message-Gas=1584 16480 68342 ns/op 1656 B/op 17 allocs/op
BenchmarkPrecompiledEd25519Verify/invalid_input_malformed_public_key-Gas=1584 164198 7205 ns/op 1384 B/op 16 allocs/op
BenchmarkPrecompiledEd25519Verify/invalid_input_malformed_signature-Gas=1584 17793 69061 ns/op 1656 B/op 17 allocs/op
BenchmarkPrecompiledEd25519Verify/invalid_input_msg_too_short-Gas=1584 17588 68460 ns/op 1656 B/op 17 allocs/op
BenchmarkPrecompiledEd25519Verify/invalid_input_empty-Gas=1560 387652 2887 ns/op 1368 B/op 15 allocs/op
PASS
ok github.com/ethereum/go-ethereum/core/vm 13.934s
In summary:
1 word = 67400 ns/op
2 word = 67768 ns/op (368 ns/op slower than 1 word)
4 word = 68614 ns/op (846 ns/op slower than 1 word)
$ GOMAXPROCS=1 go test -cpu 1 -bench=BenchmarkPrecompiledEd25519Verify -benchmem
goos: linux
goarch: amd64
pkg: github.com/ethereum/go-ethereum/core/vm
BenchmarkPrecompiledEd25519Verify/valid_input-Gas=1584 9183 133197 ns/op 1656 B/op 17 allocs/op
BenchmarkPrecompiledEd25519Verify/valid_input-Gas=1608 7893 134578 ns/op 1656 B/op 17 allocs/op
BenchmarkPrecompiledEd25519Verify/valid_input_4_word_msg-Gas=1632 9027 134921 ns/op 1656 B/op 17 allocs/op
BenchmarkPrecompiledEd25519Verify/valid_input_with_empty_message-Gas=1584 9013 134856 ns/op 1656 B/op 17 allocs/op
BenchmarkPrecompiledEd25519Verify/invalid_input_malformed_public_key-Gas=1584 103746 11533 ns/op 1368 B/op 15 allocs/op
BenchmarkPrecompiledEd25519Verify/invalid_input_malformed_signature-Gas=1584 9060 135270 ns/op 1656 B/op 17 allocs/op
BenchmarkPrecompiledEd25519Verify/invalid_input_msg_too_short-Gas=1584 8984 139509 ns/op 1656 B/op 17 allocs/op
BenchmarkPrecompiledEd25519Verify/invalid_input_empty-Gas=1560 430832 2867 ns/op 1368 B/op 15 allocs/op
PASS
ok github.com/ethereum/go-ethereum/core/vm 10.036s
In summary:
1 word = 133197 ns/op
2 word = 134578 ns/op (1381ns slower than 1 word)
4 word = 134921 ns/op (343ns slower than 2 word)
[admin@laptop vm]$ GOMAXPROCS=1 go test -cpu 1 -bench=BenchmarkPrecompiledEd25519Verify -benchmem
goos: linux
goarch: amd64
pkg: github.com/ethereum/go-ethereum/core/vm
BenchmarkPrecompiledEd25519Verify/valid_input-Gas=1584 12343 95035 ns/op 1656 B/op 17 allocs/op
BenchmarkPrecompiledEd25519Verify/valid_input-Gas=1608 12684 95417 ns/op 1656 B/op 17 allocs/op
BenchmarkPrecompiledEd25519Verify/valid_input_4_word_msg-Gas=1632 12546 95767 ns/op 1656 B/op 17 allocs/op
BenchmarkPrecompiledEd25519Verify/valid_input_with_empty_message-Gas=1584 12489 95941 ns/op 1656 B/op 17 allocs/op
BenchmarkPrecompiledEd25519Verify/invalid_input_malformed_public_key-Gas=1584 134200 8228 ns/op 1432 B/op 16 allocs/op
BenchmarkPrecompiledEd25519Verify/invalid_input_malformed_signature-Gas=1584 12519 95889 ns/op 1656 B/op 17 allocs/op
BenchmarkPrecompiledEd25519Verify/invalid_input_msg_too_short-Gas=1584 12375 97944 ns/op 1656 B/op 17 allocs/op
BenchmarkPrecompiledEd25519Verify/invalid_input_empty-Gas=1560 426584 2935 ns/op 1368 B/op 15 allocs/op
PASS
ok github.com/ethereum/go-ethereum/core/vm 15.679s
In summary:
1 word = 95035 ns/op
2 word = 95417 ns/op (382ns slower than 1 word)
4 word = 95767 ns/op (350ns slower than 2 word)
1 word = 133197 ns/op vs 95035 ns/op (oasis's 40% faster)
2 word = 134578 ns/op vs 95417 ns/op (oasis's 41% faster)
4 word = 134921 ns/op vs 95767 ns/op (oasis's 40% faster)
1 word = 67400 ns/op vs 95035 ns/op (hdevalence's 41% faster)
2 word = 67768 ns/op vs 95417 ns/op (hdvalence's 40% faster)
4 word = 68614 ns/op vs 95767 ns/op (hdvalence's 39% faster)
$ GOMAXPROCS=1 go test -cpu 1 -bench=BenchmarkPrecompiledEcrecover -benchmem
goos: linux
goarch: amd64
pkg: github.com/ethereum/go-ethereum/core/vm
BenchmarkPrecompiledEcrecover/-Gas=3000 8944 145887 ns/op 2632 B/op 22 allocs/op
PASS
ok github.com/ethereum/go-ethereum/core/vm 1.494s
Based on the presented benchmark, I conclude that:
- the bigger the message the slower (by ns/op) execution time. However the difference between 1,2 or 4 word (where the word is 64byte) is not linear,
- the gas price is amortized by the sha512 per-word cost which seems to be a fair calculation (to be adjusted if needed)
- 1-word test in benchmark 0 (hdvalence) takes 67400 ns/op + 1584 gas where comparison function (ecrecover) runs in 145887 ns/op + 3000 gas. So, ed25519 is ~116% faster, also the memory usage is ~37% lower (1656 vs 2632 B/op)
Taking the benchmark results into consideration we think the hdvalence is the best choice performance-wise.
pragma solidity ^0.5.12;
contract Console {
event LogBytes32(string, bytes32);
function log(string memory s , bytes32 x) public {
emit LogBytes32(s, x);
}
event LogBytes(string, bytes);
function log(string memory s , bytes memory x) public {
emit LogBytes(s, x);
}
}
contract Demo is Console {
function callEd25519Verify(bytes memory message, bytes32 publicKey, bytes32[2] memory signature) public returns (bytes32 result) {
bytes memory data = new bytes(publicKey.length + 2 * 32);
uint i;
uint idx = 0;
for( i = 0; i < 32; i++ ) {
data[idx++] = publicKey[i];
}
for( i = 0; i < 2; i++ ) {
uint j = 0;
for( j = 0; j < 32; j++ ) {
data[idx++] = signature[i][j];
}
}
bytes memory all = abi.encodePacked(data, message);
assembly {
let len := mload(all)
let success := call(gas, 0x0a, 0, add(all, 0x20), len, result, 0x20)
switch success
case 0 {
revert(0,0)
} default {
result := mload(result)
}
}
log("input", all);
log("result", result);
return result;
}
}
Valid Input:
"0x74657374206d657373616765","0x304b6bb12f4dcffd4b147881bdeebfc63b7fb61412a3b696a530df076dde0546",["0xb3624c07c18b1abdb8f4808f0115e6a33d7323ac821976479cfae8426e86c178","0xbd32dacce8f0d52456da8dfaf88cb42f352679674f9d4980635c9c686c6c560d"]
where this is concatenated version passed to precompiled code:
0x304b6bb12f4dcffd4b147881bdeebfc63b7fb61412a3b696a530df076dde0546b3624c07c18b1abdb8f4808f0115e6a33d7323ac821976479cfae8426e86c178bd32dacce8f0d52456da8dfaf88cb42f352679674f9d4980635c9c686c6c560d74657374206d657373616765
Transaction logs with valid data:
[
{
"from": "0xcC10Fc73910D244c4458020BaF43dF6DD43132c0",
"topic": "0xe8407a0209fa99ec3a7228aff140c3d3e68bd279391739c7e0b65cd406cc93b5",
"event": "LogBytes",
"args": {
"0": "input",
"1": "0x304b6bb12f4dcffd4b147881bdeebfc63b7fb61412a3b696a530df076dde0546b3624c07c18b1abdb8f4808f0115e6a33d7323ac821976479cfae8426e86c178bd32dacce8f0d52456da8dfaf88cb42f352679674f9d4980635c9c686c6c560d74657374206d657373616765"
}
},
{
"from": "0xcC10Fc73910D244c4458020BaF43dF6DD43132c0",
"topic": "0x02d93529bba9d141e5e06733c52c7e6fbcb1149586adb5c24064b522ab26f1d7",
"event": "LogBytes32",
"args": {
"0": "result",
"1": "0x0000000000000000000000000000000000000000000000000000000000000000"
}
}
]
Transaction logs with invalid data:
[
{
"from": "0xcC10Fc73910D244c4458020BaF43dF6DD43132c0",
"topic": "0xe8407a0209fa99ec3a7228aff140c3d3e68bd279391739c7e0b65cd406cc93b5",
"event": "LogBytes",
"args": {
"0": "input",
"1": "0x304b6bb12f4dcffd4b147881bdeebfc63b7fb61412a3b696a530df076dde0546b3624c07c18b1abdb8f4808f0115e6a33d7323ac821976479cfae8426e86c178bd32dacce8f0d52456da8dfaf88cb42f352679674f9d4980635c9c686c6c560d64257374206d657373616765"
}
},
{
"from": "0xcC10Fc73910D244c4458020BaF43dF6DD43132c0",
"topic": "0x02d93529bba9d141e5e06733c52c7e6fbcb1149586adb5c24064b522ab26f1d7",
"event": "LogBytes32",
"args": {
"0": "result",
"1": "0x0000000000000000000000000000000000000000000000000000000000000001"
}
}
]
curl localhost:8545 \
-X POST \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "eth_call",
"params": [
{
"to": "0x000000000000000000000000000000000000000a",
"data": "0x304b6bb12f4dcffd4b147881bdeebfc63b7fb61412a3b696a530df076dde0546b3624c07c18b1abdb8f4808f0115e6a33d7323ac821976479cfae8426e86c178bd32dacce8f0d52456da8dfaf88cb42f352679674f9d4980635c9c686c6c560d74657374206d657373616765"
},
"latest"
],
"id": 1
}'
{"jsonrpc":"2.0","id":1,"result":"0x0000000000000000000000000000000000000000000000000000000000000000"}
curl localhost:8545 \
-X POST \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "eth_call",
"params": [
{
"to": "0x000000000000000000000000000000000000000a",
"data": "0x204b6bb12f4dcffd4b147881bdeebfc63b7fb61412a3b696a530df076dde0546b3624c07c18b1abdb8f4808f0115e6a33d7323ac821976479cfae8426e86c178bd32dacce8f0d52456da8dfaf88cb42f352679674f9d4980635c9c686c6c560d74657374206d657373616765"
},
"latest"
],
"id": 1
}'
{"jsonrpc":"2.0","id":1,"result":"0x0000000000000000000000000000000000000000000000000000000000000001"}