ECDSA signature verification
Description
This task is an example on how to compute a signature verification using the ECDSA algorithm, the one used in EVM blockchains like Ethereum, Polygon, Hyperledger Besu, Avalanche C-Chain and others. It takes several paramaters as an input, all of them contained in an input.txt file provided as example:
Test string that was signed
Signature with the r, s, v components
Address that signed the message
It will return a boolean indicating if the signature validates correctly
Using Truebit to Execute ECDSA verification example
In order to get a verified result, we can use Truebit to execute the sign-verify task.
The code
You can find the ECDSA signature verification example within the folder truebit-nextgen-examples/function-tasks/js/ecdsa-sign-verify
📁 src └─📄 ecdsa-verify.js 📁 dist └─main.js 📄 package-lock.json 📄 package.json 📄 rollup.config.mjs
Source
ecdsa-verify.js
/*
Signee address recoverer and validator
======================================
This task is an example on how to compute a signature verification using the ECDSA algorithm, the one used in EVM blockchains like Ethereum, Polygon, Hyperledger Besu, Avalanche C-Chain and others. It takes several paramaters as an input, all of them contained in an input.txt file provided as example:
Input parameters (input.txt)
----------------------------
A json-formatted file. See the example below:
{
"messageHash": "0x13260446136602c31107aa5e08571c276026b60c5156c1a564e268f3ab08492c",
"signature": {
"r": "0x7097b9a0810d13e60182f261708c2c260e86ea09853cd48b184a5a4b4ea08c02",
"s": "0x087bd86b8cdee3aec88ac365ec533c9a111e5b5586c4941a790ca46970c3f380",
"v": 28
},
"address": "0x63f9a92d8d61b48a9fff8d58080425a3012d05c8"
}
Fields:
messageHash: "0x" + hex-encoded data (ASCII string, 66 chars).
signature: ECDSA signature, divided in its 'r', 's' and 'v'
components, as specified below.
r: "0x" + hex-encoded data (ASCII string, 64 chars).
s: "0x" + hex-encoded data (ASCII string, 64 chars).
v: numeric parameter.
address: "0x" + hex-encoded data (ASCII string, 42 chars).
Output values (output.txt)
--------------------------
A JSON-formatted file. See the example below:
{
"declaredAddress": "0x63f9a92d8d61b48a9fff8d58080425a3012d05c8",
"derivedAddress": "0x63f9a92d8d61b48a9fff8d58080425a3012d05c8",
"match": true
}
*/
const fs = require('fs');
const elliptic = require('elliptic');
const keccak256 = require('js-sha3').keccak256;
const bn = require('bn.js');
function checkSignature(messageHash, signature, address) {
const ec = new elliptic.ec('secp256k1');
const bnHash = new bn(messageHash, 16);
const recoveryByteV = signature.v - 27;
const pubKeyRecovered = ec.recoverPubKey(bnHash, signature, recoveryByteV).encode('hex');
const pubKeyHash = keccak256(Buffer.from(pubKeyRecovered.slice(2), 'hex'));
const derivedAddress = pubKeyHash.slice(-40);
return {
declaredAddress: '0x' + address,
derivedAddress: '0x' + derivedAddress,
match: address === derivedAddress
}
}
function fixHash(input, key) {
if (typeof input[key] !== 'string') {
throw (`invalid type on input key '${key}', not a string.`);
}
input[key] = input[key].replace('0x', '').toLowerCase();
}
const main = () => {
const inputJson = fs.readFileSync('input.txt', 'utf-8');
let inputObj = JSON.parse(inputJson);
fixHash(inputObj, 'messageHash');
fixHash(inputObj.signature, 'r');
fixHash(inputObj.signature, 's');
fixHash(inputObj, 'address');
const outputObj = checkSignature(inputObj.messageHash, inputObj.signature, inputObj.address);
const outputJson = JSON.stringify(outputObj, null, 4);
fs.writeFileSync('output.txt', outputJson);
console.log(outputJson);
}
main();
Recommended configuration for Rollup tool (file rollup.config.mjs):
import nodeResolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import pluginJson from '@rollup/plugin-json';
export default [
{
input: 'src/ecdsa-verify.js',
output: {
file: 'dist/main.js',
format: 'es',
name: 'ecdsa',
},
plugins: [
nodeResolve(),
commonjs(),
pluginJson(),
],
}
]
In/Out parameters
In order to send parameters to the Truebit task, you need to open the "input.txt" file and get the value from there. For now, only one input parameter is allowed.
inputJson = fs.readFileSync('input.txt', 'utf-8');
In order to retrieve the output value from the Truebit task, you need save it in the "output.txt" file.
fs.writeFileSync('output.txt', outputJson);
Try out
We will use the Truebit CLI to compile and try our source code. Once the code is finished, we will deploy it to the coordination hub so that everyone who knows the namespace
and taskname
.
Step 1: Create the ecdsa-verify source code
You will find a file called main.js within the folder /dist (This is the file generated by the Rollup tool that will be used for testing purposes)
Step 2: Build the source code
Execute the build command against Truebit node to get an instrumented Truebit task
truebit build truebit-nextgen-examples/function-tasks/js/ecdsa-sign-verify/dist -l js
Output
Building the function task
Your function task is ready.
Use the following TaskId for execution or deployment:
js_70f82c303f73ab09ed68dc0fc44829440e8c1838a0b7e0d931dd25e6835be8c2
Step 3: Try the code
Execute the start command against the Truebit node to test our Algorithm. You will need to submit the instrumented task id + the input parameter.
In this case, the input parameter is a file containing the address, signature and test data that we want to verify. Please use the file called input.txt located within the root folder.
truebit start js_70f82c303f73ab09ed68dc0fc44829440e8c1838a0b7e0d931dd25e6835be8c2 "$(cat truebit-nextgen-examples/function-tasks/js/ecdsa-sign-verify/input.txt)"
Output
Executing the function Task
Execution Id: 72f88499-efa3-4b8d-be69-cf6f6e3a40a4
Task executed with status: succeed
Task executed with result: true
Task resource usage:
┌───────────────┬───────────────┬───────────────┬───────┬─────────────┬──────────┐
│ (index) │ Unit │ Limits │ Peak │ Last status │ Capacity │
├───────────────┼───────────────┼───────────────┼───────┼─────────────┼──────────┤
│ Gas │ 'Steps' │ 1099511627776 │ 'N/A' │ 16228464673 │ 68 │
│ Nested calls │ 'Calls' │ 65536 │ 9336 │ 9304 │ 7 │
│ Frame memory │ 'Bytes' │ 524288 │ 29339 │ 27929 │ 18 │
│ System memory │ '64 Kb pages' │ 2048 │ 119 │ '𝚫 1' │ 17 │
└───────────────┴───────────────┴───────────────┴───────┴─────────────┴──────────┘
Step 4: Deploy the task
Last, but not least, Execute the deploy command to deploy our task to the coordination hub, so that anyone with the namespace, taskname and the API key can execute it.
truebit deploy <namespace> <taskname> --taskId js_70f82c303f73ab09ed68dc0fc44829440e8c1838a0b7e0d931dd25e6835be8c2
Output
The function task has been deployed successfully.
The Task <taskName> version <version> was registered successfully in namespace <namespace>
Last updated
Was this helpful?