Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ethereum): ✨multiple ethereum signatures verification #708

Merged
merged 12 commits into from
Aug 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions docs/examples/zencode_cookbook/ethereum/signature_array.data
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"addresses_signatures": [
{
"address": "0x2B8070975AF995Ef7eb949AE28ee7706B9039504",
"signature": {
"r": "ed8f36c71989f8660e8f5d4adbfd8f1c0288cca90d3a5330b7bf735d71ab52fe",
"s": "7ba0a7827dc4ba707431f1c10babd389f658f8e208b89390a9be3c097579a2ff",
"v": "27"
}
},
{
"address": "0x3028806AC293B5aC9b863B685c73813626311DaD",
"signature": {
"r": "40d305373c648bb6b2bbadebe02ada256a9d0b3d3c37367c0a2795e367b22f73",
"s": "72e40dfc3497927764d1585783d058e4367bb4d24d2107777d7aa4ddcb6593c7",
"v": "27"
}
},
{
"address": "0xe1C2F1ACb2758c4D88EDb84e0306A0a96682E62a",
"signature": {
"r": "9e07477c31db612e8c99a950385162373ff41a5b8941470b1aeba43b76c53570",
"s": "05fce6615567dc1944cc02fbed86202b09d92d79fbade425af0d74c328d8f6ae",
"v": "28"
}
}
],
"signed_string": "I love the Beatles, all but 3"
}
7 changes: 7 additions & 0 deletions docs/examples/zencode_cookbook/ethereum/signature_array.zen
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Scenario ethereum: verify sig
Given I have a 'ethereum address signature pair array' named 'addresses_signatures'
Given I have a 'string' named 'signed_string'

When I verify the ethereum address signature pair array 'addresses_signatures' of 'signed_string'

Then print the string 'ok'
37 changes: 37 additions & 0 deletions docs/examples/zencode_cookbook/ethereum/verification_result.data
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"addresses_signatures": [
{
"address": "0x2B8070975AF995Ef7eb949AE28ee7706B9039504",
"signature": {
"r": "ed8f36c71989f8660e8f5d4adbfd8f1c0288cca90d3a5330b7bf735d71ab52fe",
"s": "7ba0a7827dc4ba707431f1c10babd389f658f8e208b89390a9be3c097579a2ff",
"v": "27"
}
},
{
"address": "0x3028806AC293B5aC9b863B685c73813626311DaD",
"signature": {
"r": "40d305373c648bb6b2bbadebe02ada256a9d0b3d3c37367c0a2795e367b22f73",
"s": "72e40dfc3497927764d1585783d058e4367bb4d24d2107777d7aa4ddcb6593c7",
"v": "27"
}
},
{
"address": "0xe1C2F1ACb2758c4D88EDb84e0306A0a96682E62a",
"signature": {
"r": "9e07477c31db612e8c99a950385162373ff41a5b8941470b1aeba43b76c53570",
"s": "05fce6615567dc1944cc02fbed86202b09d92d79fbade425af0d74c328d8f6ae",
"v": "28"
}
},
{
"address": "0xe1C2F1ACb2758c4D88EDb84e0306A0a96682E62a",
"signature": {
"r": "9e07477c31db612e8c99a950385162373ff41a5b8941470b1aeba43b76c53570",
"s": "05fce6615567dc1944cc02fbed86202b09d92d79fbade425af0d74c328d8f6ae",
"v": "27"
}
}
],
"signed_string": "I love the Beatles, all but 3"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"result_array":[{"address":"0x2B8070975AF995Ef7eb949AE28ee7706B9039504","status":"verified"},{"address":"0x3028806AC293B5aC9b863B685c73813626311DaD","status":"verified"},{"address":"0xe1C2F1ACb2758c4D88EDb84e0306A0a96682E62a","status":"verified"},{"address":"0xe1C2F1ACb2758c4D88EDb84e0306A0a96682E62a","status":"not verified"}]}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Scenario ethereum: verify sig
Given I have a 'ethereum address signature pair array' named 'addresses_signatures'
Given I have a 'string' named 'signed_string'

When I use the ethereum address signature pair array 'addresses_signatures' to create the result array of 'signed_string'

Then print the 'result_array'
22 changes: 22 additions & 0 deletions docs/pages/zencode-scenarios-ethereum.md
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,28 @@ When the signature is correct, the output will be:

[](../_media/examples/zencode_cookbook/ethereum/doc_verifytesteth_str_out.json ':include :type=code json')

## Multiple signatures verification

Given an array that contains pairs of ethereum address and signature and a signed message

[](../_media/examples/zencode_cookbook/ethereum/signature_array.data ':include :type=code json')

anyone may verify all the signatures usign the following script:

[](../_media/examples/zencode_cookbook/ethereum/signature_array.zen ':include :type=code gherkin')

This code will fail if at least one signature is not verified, to obtain a list that associate each address to the result of the verification the folowing script can be used:

[](../_media/examples/zencode_cookbook/ethereum/verification_result.zen ':include :type=code gherkin')

For example with the following data, where the last signature is the copy of the third one in which the *v* parameter is set to 27 instead of 28, *i.e.* is not a valid signature:

[](../_media/examples/zencode_cookbook/ethereum/verification_result.data ':include :type=code json')

the result is

[](../_media/examples/zencode_cookbook/ethereum/verification_result.out ':include :type=code json')

## Sign complex object

It could be the case that one wants to sign an object different from a string or a transaction.
Expand Down
61 changes: 57 additions & 4 deletions src/lua/zencode_ethereum.lua
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,20 @@ local function export_method_f(obj)
return res
end

local function import_eth_address_signature_pair_f(obj)
local res = {}
res.address = ZEN.get(obj, 'address', import_eth_address_f, tostring)
res.signature = import_signature_f(obj.signature)
return res
end

local function export_eth_address_signature_pair_f(obj)
local res = {}
res.address = ETH.checksum_encode(obj.address)
res.signature = export_signature_f(obj.signature)
return res
end

ZEN.add_schema(
{
ethereum_public_key = { import = O.from_hex,
Expand Down Expand Up @@ -171,6 +185,10 @@ ZEN.add_schema(
export = export_signature_f},
ethereum_method = { import = import_method_f,
export = export_method_f},
ethereum_address_signature_pair = {
import = import_eth_address_signature_pair_f,
export = export_eth_address_signature_pair_f
},
})

When('create the ethereum key', function()
Expand Down Expand Up @@ -420,18 +438,53 @@ When("create the ethereum signature of ''", function(object)
end)

IfWhen("verify the '' has a ethereum signature in '' by ''", function(doc, sig, by)

local msg = have(doc)
local ethersMessage = O.from_string("\x19Ethereum Signed Message:\n") .. O.new(#msg) .. msg
local hmsg = keccak256(ethersMessage)

local signature = have(sig)
local address = have(by)
ZEN.assert(ETH.verify_signature_from_address(signature, address, fif(signature.v:parity(), 0, 1), hmsg),
'The ethereum signature by '..by..' is not authentic')
end)

ZEN.assert(
ETH.verify_signature_from_address(signature, address, fif(signature.v:parity(), 0, 1), hmsg),
'The ethereum signature by '..by..' is not authentic'
local function _verify_address_signature_array(add_sig, doc, fun)
local msg = have(doc)
local ethersMessage = O.from_string("\x19Ethereum Signed Message:\n") .. O.new(#msg) .. msg
local hmsg = keccak256(ethersMessage)

local address_signature, address_signature_codec = have(add_sig)
ZEN.assert(address_signature_codec.schema, "The ethereum address signature pair array is not a schema")
ZEN.assert(address_signature_codec.zentype == "a", "The ethereum address signature pair array is not an array")
return fun(address_signature, hmsg)
end

IfWhen("verify the ethereum address signature pair array '' of ''", function(add_sig, doc)
_verify_address_signature_array(add_sig, doc,
function(address_signature_pair, hmsg)
for _, v in pairs(address_signature_pair) do
ZEN.assert(ETH.verify_signature_from_address(v.signature, v.address, fif(v.signature.v:parity(), 0, 1), hmsg),
'The ethereum signature by '..ETH.checksum_encode(v.address)..' is not authentic')
end
end
)
end)

IfWhen("use the ethereum address signature pair array '' to create the result array of ''", function(add_sig, doc)
empty 'result array'
ACK.result_array = _verify_address_signature_array(add_sig, doc,
function(address_signature_pair, hmsg)
local res = {}
for _, v in pairs(address_signature_pair) do
local tmp = {}
tmp.address = O.from_string(ETH.checksum_encode(v.address))
tmp.status = ETH.verify_signature_from_address(v.signature, v.address, fif(v.signature.v:parity(), 0, 1), hmsg) and O.from_string("verified") or O.from_string("not verified")
table.insert(res, tmp)
end
return res
end
)
new_codec("result array", {encoding="string"})
end)

When("use the ethereum transaction to run '' using ''", function(m, p)
Expand Down
18 changes: 18 additions & 0 deletions src/lua/zenroom_common.lua
Original file line number Diff line number Diff line change
Expand Up @@ -316,3 +316,21 @@ function deprecated(old, new, func)
end
return old, warn_func
end

function zip(...)
local arrays = {...}
local ans = {}
local index = 0
for k, v in pairs(arrays) do
ZEN.assert(isarray(v), "zip input are not arrays")
end
return function()
index = index + 1
for k, v in pairs(arrays) do
ans[k] = v[index]
-- stop iteration at the first nil value in one of the array
if ans[k] == nil then return end
end
return table.unpack(ans)
end
end
8 changes: 8 additions & 0 deletions test/benchmark/eth/lua_for/batch_verification.zen.bench
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Scenario 'ethereum': batch verification

Given I have a 'ethereum address signature pair array' named 'address_signature_pair'
and I have a 'string' named 'message'

When I verify the ethereum address signature pair array 'address_signature_pair' of 'message'

Then print the string 'OK'
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Scenario ethereum: verify sig
Given I have a 'ethereum address signature pair array' named 'address_signature_pair'
and I have a 'string' named 'message'

When I use the ethereum address signature pair array 'address_signature_pair' to create the result array of 'message'

Then print the 'result array'
54 changes: 54 additions & 0 deletions test/benchmark/eth/lua_for/benchmark_sign.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#!/usr/bin/env bash

n_signatures=1000

if [ $# != 1 ]; then
printf "Generating $n_signatures signatures and verify them.\nIf you want to use a different number use: $0 <number_of_signatures>\n"
else
n_signatures=$1
echo "Generating $n_signatures signatures and verify them."
fi
msg='"message": "I love the Beatles, all but 3",'

echo
echo Signing $n_signatures times...
echo '"address_signature_pair": [' >addresses_signatures.txt
time for ((i=1; i<=$n_signatures; i++)); do
zenroom -a key_add_sign_gen.keys.bench -z key_add_sign_gen_manual.zen.bench >res_1.json 2>execution_data_1.txt
if [ "$?" != "0" ]; then
echo "Error during generation"
cat execution_data_1.txt
exit 1
fi
if [ "$i" != "$n_signatures" ]; then
cat res_1.json | cut -d: -f2- | cut -d\} -f1-2 | sed 's/$/,/' >> addresses_signatures.txt
else
cat res_1.json | cut -d: -f2- | cut -d\} -f1-2 >> addresses_signatures.txt
fi
done
echo ']' >>addresses_signatures.txt
echo "{$msg" > add_and_sign.json
cat addresses_signatures.txt >> add_and_sign.json
echo "}" >> add_and_sign.json
rm res_1.json execution_data_1.txt addresses_signatures.txt

echo
echo Verifying $n_signatures signatures...
time zenroom -a add_and_sign.json -z batch_verification.zen.bench >res_2.json 2>execution_data_2.txt
if [ "`cat res_2.json`" != "{\"output\":[\"OK\"]}" ]; then
echo "Error during verification"
cat execution_data_2.txt
exit 0
fi
rm -f res_2.json execution_data_2.txt

echo
echo Create the verification result of $n_signatures signatures...
time zenroom -a add_and_sign.json -z batch_verification_result.zen.bench >res_3.json 2>execution_data_3.txt
if [ "$?" != "0" ]; then
echo "Error during verification result creation"
cat execution_data_3.txt
exit 0
fi
cat res_3.json | grep "not verified"
rm -f res_3.json execution_data_3.txt add_and_sign.json
8 changes: 8 additions & 0 deletions test/benchmark/eth/lua_for/key_add_sign_gen.keys.bench
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"message": "I love the Beatles, all but 3",
"start": 1,
"step": 1,
"ethereum_addresses": [],
"ethereum_signatures": [],
"n_signatures": "10000"
}
24 changes: 24 additions & 0 deletions test/benchmark/eth/lua_for/key_add_sign_gen.zen.bench
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
Scenario 'ethereum': keys and sign

Given I have a 'number' named 'n_signatures'
and I have a 'number' named 'start'
and I have a 'number' named 'step'
and I have a 'string' named 'message'

When I create the 'ethereum address array' named 'ethereum addresses'
and I create the 'ethereum signature array' named 'ethereum signatures'

Foreach 'n' in sequence from 'start' to 'n_signatures' with step 'step'
# generate
When I create the ethereum key
and I create the ethereum address
and I create the ethereum signature of 'message'
# store
When I move 'ethereum address' in 'ethereum addresses'
and I move 'ethereum signature' in 'ethereum signatures'
and I remove the 'keyring'
EndForeach

Then print the 'ethereum addresses'
and print the 'ethereum signatures'
and print the 'message'
13 changes: 13 additions & 0 deletions test/benchmark/eth/lua_for/key_add_sign_gen_manual.zen.bench
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
Scenario 'ethereum': keys and sign

Given I have a 'string' named 'message'

When I create the ethereum key
and I create the ethereum address
and I create the ethereum signature of 'message'

When I create the 'ethereum_address_signature_pair' named 'signature'
and I move the 'ethereum_address' to 'address' in 'signature'
and I move the 'ethereum_signature' to 'signature' in 'signature'

Then print the 'signature'
12 changes: 12 additions & 0 deletions test/benchmark/eth/zencode_foreach/batch_verification.zen.bench
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
Scenario 'ethereum': batch verification

Given I have a 'ethereum address array' named 'ethereum addresses'
and I have a 'ethereum signature array' named 'ethereum signatures'
and I have a 'string' named 'message'

Foreach 'address' in 'ethereum addresses'
and 'signature' in 'ethereum signatures'
When I verify the 'message' has a ethereum signature in 'signature' by 'address'
EndForeach

Then print the string 'OK'
29 changes: 29 additions & 0 deletions test/benchmark/eth/zencode_foreach/benchmark_sign.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#!/usr/bin/env bash

n_signatures=1000

if [ $# != 1 ]; then
echo "Generating $n_signatures signatures and verify them.\nIf you want to use a different number use: $0 <number_of_signatures>"
else
n_signatures=$1
echo "Generating $n_signatures signatures and verify them."
fi
max_iter=$(($n_signatures+3))
tmp=mktemp
jq --arg value $n_signatures '.n_signatures = $value' key_add_sign_gen.keys.bench > $tmp && mv $tmp key_add_sign_gen.keys.bench

echo signing $n_signatures times...
zenroom -c maxiter=dec:$max_iter -a key_add_sign_gen.keys.bench -z key_add_sign_gen.zen.bench > add_and_sign.json 2>execution_data_1.txt
time_used_1=`cat execution_data_1.txt | grep "Time used" | cut -d: -f2`
echo generate: $time_used_1 microseconds

echo verifying $n_signatures signatures...
zenroom -c maxiter=dec:$max_iter -a add_and_sign.json -z batch_verification.zen.bench >res.json 2>execution_data.txt
if [ "`cat res.json`" != "{\"output\":[\"OK\"]}" ]; then
echo "Error during verification"
cat execution_data.txt
exit 0
fi
time_used=`cat execution_data.txt | grep "Time used" | cut -d: -f2`
echo verify: $time_used microseconds
rm -f res.json execution_data.txt add_and_sign.json execution_data_1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"start": 1,
"step": 1,
"message": "I love the Beatles, all but 3",
"n_signatures": "10000"
}
Loading
Loading