Inconsistent behavior of web3.eth.sign between ganache-cli and hdwallet-provider
See original GitHub issue- Question about this asked on gitter: [x]
- Link to the question on gitter: https://gitter.im/ConsenSys/truffle?at=5ecd5c0f778fad0b1329b1c0
Expected Behavior
The output of web3.eth.sign should be consistent when using different Ethereum clients, including Ganache.
Current Behavior
The output of web3.eth.sign when using Ganache is different from that of other Ethereum client implementations in that the last byte of the signature (v) is either 00 or 01, while in other implementations it’s 1b or 1c. Notably, the behavior of web3.eth.sign when only using Ganache is inconsistent with that of @truffle/hdwallet-provider.
Note that this inconsistency is not limited to Truffle projects.
- The current example in the Ethereum Wiki however shows that the last output byte resulting from an RPC call to
eth_signis1b(Ganache would return00instead). The behavior was apparently different in previous versions of this documentation (source). - The current examples in the documentation of Web3.js show that the last output byte of a call to
web3.eth.signis00/01. - Calling
eth_signin Parity/OpenEthereum returns1b/1c, as well as callingweb3.eth.signwith Truffle using Parity/OpenEthereum as its client. - Calling
eth_signin Geth returns1b/1c.
Possible Solution
Ganache and @truffle/hdwallet-provider should settle on a single standard output of web3.eth.sign.
I would recommend changing Ganache’s behavior so that the final byte of an RPC call to eth_sign is 1b or 1c for consistency with Geth and Parity/OpenEthereum. This change would modify the last byte returned by web3.eth.sign accordingly, since it’s just a wrapper around eth_sign.
Steps to Reproduce
Install truffle, ganache-cli, @truffle/hdwallet-provider.
Run ganache-cli --mnemonic="shove shoulder neutral steak day correct neither girl alcohol modify bacon fee"
Create a new Truffle project with the following network configuration:
var HDWalletProvider = require("@truffle/hdwallet-provider");
var mnemonic = "shove shoulder neutral steak day correct neither girl alcohol modify bacon fee";
var provider = new HDWalletProvider(mnemonic, "http://localhost:8545");
module.exports = {
networks: {
ganache: {
host: "127.0.0.1",
port: 8545,
network_id: "*"
},
hdwalletprovider: {
provider,
network_id: "*"
}
}
};
Create Truffle script script.js with the following content:
module.exports = async (callback, b) => {
const accounts = await web3.eth.getAccounts();
console.log(await web3.eth.sign("test string", accounts[0]));
callback();
};
Test the script on both networks:
$ npx truffle exec script.js --network=ganache
Using network 'ganache'.
0x0bef479e20b9186de969a6b1054892d007b870e8086cc162e0b72508a08742ba3830b16c42816dfb0420ae1f509086587971b0c0ada01fff6b17a91e2fddad8a00
$ npx truffle exec script.js --network=hdwalletprovider
Using network 'hdwalletprovider'.
0x0bef479e20b9186de969a6b1054892d007b870e8086cc162e0b72508a08742ba3830b16c42816dfb0420ae1f509086587971b0c0ada01fff6b17a91e2fddad8a1b
Observe that the final bytes differs (00 vs. 1b).
Context
This issue was found out while debugging an inconsistent behavior between production and testing in a script relying on web3.eth.sign, see this PR for details.
Your Environment
Truffle v5.1.27 (core: 5.1.27) Solidity v0.5.16 (solc-js) Node v12.16.3 Web3.js v1.2.1 Ganache-cli 6.9.1 @truffle/hdwallet-provider 1.0.35
If the changes I propose in this issue were greenlighted, I’d try to submit a PR implementing them.
Issue Analytics
- State:
- Created 3 years ago
- Reactions:5
- Comments:5 (3 by maintainers)

Top Related StackOverflow Question
Oh wow, I believe your are correct. Well get this fixed asap (which will be next week)!
As far as I know message signatures do not depend on the chain id like it is the case for Ganache. To test this I run the signing code on a Rinkeby Geth node (version 1.10.12-stable, chainId 4) and the returned signature is still ending in
1b(and1c). If the implementation depended on the chain id I’d expect thevvalue to somehow include the chain id4.In particular,
eth_signsignatures can easily be replayed across networks, this is why other signing implementations like EIP-712 recommend to include the chain id in the signed data. Changing just thevvalue in any case wouldn’t help as we can just replace it with the correspondingvvalue for mainnet and get a valid mainnet signature.Note that here we are talking about message signing and not transaction signing, so even if likely related the issue that was just linked to this is different.