Skip to content

Instantly share code, notes, and snippets.

@karzak
Last active December 6, 2018 18:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save karzak/d00b765a7648a7942cc9b82b8836a1de to your computer and use it in GitHub Desktop.
Save karzak/d00b765a7648a7942cc9b82b8836a1de to your computer and use it in GitHub Desktop.
ECDSA Sign and Recover Benchmarks
import * as Benchmarkify from "benchmarkify";
import Web3 = require("web3");
import {
instantiateSecp256k1,
Secp256k1,
RecoverableSignature
} from "bitcoin-ts";
import EthCrypto from 'eth-crypto';
import { keccak256 } from "js-sha3";
import secp256k1 = require("secp256k1");
interface Secp256k1RecoverableSignature {
signature: Buffer;
recovery: number;
}
const TARGET_SIGNATURE =
"4df9cd2743af4edcae02ee70dfa95cc3d61bcfad788b7872a82507f2403d7d5748bcdaa0c728f7f99a91a4999b9afbf7f2974649d987eb80c7c737afee192290";
const TARGET_ADDRESS = "c9039ceEc50B2ae3907756cB0CC5419B3c8696d9".toLowerCase();
const web3 = new Web3("wss://kovan.infura.io/ws");
const web3PrivKey =
"0X" + "FE2DD43AAB8D2414AAC2DADD6A9B513E0B4C02A7328B45D09C73031140EEAADD";
const address = web3.eth.accounts.wallet.add(web3PrivKey).address;
const message = "some data";
const prefix = "\x19Ethereum Signed Message:\n";
const privKey = new Buffer(
"FE2DD43AAB8D2414AAC2DADD6A9B513E0B4C02A7328B45D09C73031140EEAADD",
"hex"
);
const messageBuffer = new Buffer(
keccak256(prefix + message.length + message),
"hex"
);
const messageBufferString = messageBuffer.toString('hex')
let scope = {
secp256k1bts: null as Secp256k1,
web3SignedMessage: null as string,
web3SignedMessageAlternate: null as string,
secp256k1Signature: null as Secp256k1RecoverableSignature,
secp256k1btsSignature: null as RecoverableSignature,
ethCryptoSignedMessage: null as string
};
const CONFIG = {
// duration of each cycle
time: 1000,
// run suite functions only once, useful if you want to console.log
once: process.argv.includes("--once")
};
async function setup() {
scope.secp256k1bts = await instantiateSecp256k1();
scope.secp256k1btsSignature = scope.secp256k1bts.signMessageHashRecoverableCompact(
privKey,
messageBuffer
);
scope.secp256k1Signature = secp256k1.sign(messageBuffer, privKey);
scope.web3SignedMessage = await web3.eth.sign(message, address);
scope.web3SignedMessageAlternate = (await web3.eth.accounts.sign(message, web3PrivKey)).signature
//@ts-ignore
scope.ethCryptoSignedMessage = EthCrypto.sign(web3PrivKey.slice(2), messageBuffer.toString('hex'))
}
async function verify() {
console.assert(
Buffer.from(scope.secp256k1btsSignature.signature).toString("hex") ===
TARGET_SIGNATURE,
`Invalid signature for bitcoin-ts: ${Buffer.from(
scope.secp256k1btsSignature.signature
).toString("hex")}`
);
console.assert(
scope.secp256k1Signature.signature.toString("hex") === TARGET_SIGNATURE,
`Invalid signature for secp256k1: ${scope.secp256k1Signature.signature.toString(
"hex"
)}`
);
// Remove 0x prefix and recovery value suffix for web3
console.assert(
scope.web3SignedMessage.slice(2, -2) === TARGET_SIGNATURE,
`Invalid signature for web3.eth.sign: ${scope.web3SignedMessage.slice(2, -2)}`
);
console.assert(
scope.web3SignedMessageAlternate.slice(2, -2) === TARGET_SIGNATURE,
`Invalid signature for web3.eth.accounts.sign: ${scope.web3SignedMessageAlternate.slice(2, -2)}`
);
console.assert(
scope.ethCryptoSignedMessage.slice(2, -2) === TARGET_SIGNATURE,
`Invalid signature for ethCrypto.sign: ${scope.ethCryptoSignedMessage.slice(2, -2)}`
);
const bitcoinTsRecoveryAddress = keccak256(
scope.secp256k1bts
.recoverPublicKeyUncompressed(
scope.secp256k1btsSignature.signature,
scope.secp256k1btsSignature.recoveryId,
messageBuffer
)
.slice(1)
).slice(64 - 40);
const secp256k1RecoveryAddress = keccak256(
secp256k1
.recover(
messageBuffer,
scope.secp256k1Signature.signature,
scope.secp256k1Signature.recovery,
false
)
.slice(1)
).slice(64 - 40);
const ethCryptoRecoveryAddress = EthCrypto.recover(
scope.ethCryptoSignedMessage, messageBuffer.toString('hex')
)
.slice(2).toLowerCase()
const web3RecoveryAddress = web3.eth.accounts
.recover(message, scope.web3SignedMessage)
.slice(2)
.toLowerCase();
console.assert(
bitcoinTsRecoveryAddress === TARGET_ADDRESS,
`Invalid recovery address for bitcoin-ts ${bitcoinTsRecoveryAddress}`
);
console.assert(
ethCryptoRecoveryAddress === TARGET_ADDRESS,
`Invalid recovery address for ethCrypto ${ethCryptoRecoveryAddress}`
);
console.assert(
secp256k1RecoveryAddress === TARGET_ADDRESS,
`Invalid recovery address for secp256k1 ${secp256k1RecoveryAddress}`
);
console.assert(
web3RecoveryAddress === TARGET_ADDRESS,
`Invalid recovery address for web3 ${web3RecoveryAddress}`
);
}
const benchmark = new Benchmarkify()
const signSuite = benchmark.createSuite(
"ECDSA Sign",
CONFIG.once ? { time: 1, cycles: 1, minSamples: 1 } : { time: CONFIG.time }
);
signSuite.add("secp256k1.sign#test", function() {
secp256k1.sign(messageBuffer, privKey);
});
signSuite.add("ethCrypto.sign#test", function() {
EthCrypto.sign(web3PrivKey.slice(2), messageBuffer.toString('hex'));
});
signSuite.add("bitcoin-ts.sign#test", async function() {
scope.secp256k1bts.signMessageHashRecoverableCompact(privKey, messageBuffer);
});
signSuite.add("web3.eth.sign#test", async function(next) {
await web3.eth.sign(message, address);
next();
});
signSuite.add("web3.eth.accounts.sign#test", async function(next) {
await web3.eth.accounts.sign(message, web3PrivKey);
next();
});
const recoverSuite = benchmark.createSuite('ECDSA Recover',
CONFIG.once ? { time: 1, cycles: 1, minSamples: 1 } : { time: CONFIG.time }
)
recoverSuite.add('secp256k1.recoverPubkey#test', function() {
secp256k1.recover(
messageBuffer,
scope.secp256k1Signature.signature,
scope.secp256k1Signature.recovery,
false)
})
recoverSuite.add('secp256k1.recoverAddress#test', function() {
keccak256(
secp256k1.recover(
messageBuffer,
scope.secp256k1Signature.signature,
scope.secp256k1Signature.recovery,
false)
.slice(1)
)
.slice(64 - 40)
})
recoverSuite.add('ethCrypto.recoverAddress#test', function() {
EthCrypto.recover(scope.ethCryptoSignedMessage, messageBufferString)
})
recoverSuite.add('bitcoin-ts.recoverPubkey#test', function() {
scope.secp256k1bts.recoverPublicKeyUncompressed(
scope.secp256k1btsSignature.signature,
scope.secp256k1btsSignature.recoveryId,
messageBuffer)
})
recoverSuite.add('bitcoin-ts.recoverAddress#test', function() {
keccak256(
scope.secp256k1bts.recoverPublicKeyUncompressed(
scope.secp256k1btsSignature.signature,
scope.secp256k1btsSignature.recoveryId,
messageBuffer)
.slice(1)
)
.slice(64 - 40)
})
recoverSuite.add('web3.eth.accounts.recoverAddress#test', function() {
web3.eth.accounts.recover(message, scope.web3SignedMessage)
})
setTimeout(async () => {
await setup();
await verify();
let signResults = await signSuite.run()
let recoverResults = await recoverSuite.run()
}, 1000);
{
"dependencies": {
"@types/web3": "^1.0.14",
"benchmarkify": "^2.1.0",
"bitcoin-ts": "^1.4.0",
"js-sha3": "^0.8.0",
"eth-crypto": "^1.2.7",
"lodash": "^4.17.11",
"secp256k1": "^3.5.2",
"web3": "^1.0.0-beta.36"
},
"devDependencies": {
"ts-node": "^7.0.1",
"typescript": "^3.2.1"
}
}
Suite: ECDSA Sign
✔ secp256k1.sign#test 20,179 rps
✔ ethCrypto.sign#test 17,881 rps
✔ bitcoin-ts.sign#test 5,553 rps
✔ web3.eth.sign#test* 943 rps
✔ web3.eth.accounts.sign#test* 1,072 rps
secp256k1.sign#test 0% (20,179 rps) (avg: 49μs)
ethCrypto.sign#test -11.39% (17,881 rps) (avg: 55μs)
bitcoin-ts.sign#test -72.48% (5,553 rps) (avg: 180μs)
web3.eth.sign#test* -95.33% (943 rps) (avg: 1ms)
web3.eth.accounts.sign#test* -94.69% (1,072 rps) (avg: 933μs)
-----------------------------------------------------------------------
Suite: ECDSA Recover
✔ secp256k1.recoverPubkey#test 11,826 rps
✔ secp256k1.recoverAddress#test 10,825 rps
✔ ethCrypto.recoverAddress#test 8,377 rps
✔ bitcoin-ts.recoverPubkey#test 3,927 rps
✔ bitcoin-ts.recoverAddress#test 3,811 rps
✔ web3.eth.accounts.recoverAddress#test 453 rps
secp256k1.recoverPubkey#test 0% (11,826 rps) (avg: 84μs)
secp256k1.recoverAddress#test -8.47% (10,825 rps) (avg: 92μs)
ethCrypto.recoverAddress#test -29.16% (8,377 rps) (avg: 119μs)
bitcoin-ts.recoverPubkey#test -66.8% (3,927 rps) (avg: 254μs)
bitcoin-ts.recoverAddress#test -67.78% (3,811 rps) (avg: 262μs)
web3.eth.accounts.recoverAddress#test -96.17% (453 rps) (avg: 2ms)
-----------------------------------------------------------------------
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment