I am using this tutorial : https://web3py.readthedocs.io/en/stable/web3.eth.account.html#sign-a-message
## in web3py
sig = Web3.soliditySha3( [uint256, address], [tokens, contractaddress] ) ## used below also
output: HexBytes('0x3efb3cf4e41109f6f1f998401d02dbe894719a8806f45e79a5fab7d4799f00bb')
from eth_account.messages import encode_defunct
msg = sig.hex()
message = encode_defunct(text=msg)
signed_message = w3.eth.account.sign_message(message, private_key=private_key)
signed_message
SignedMessage(messageHash=HexBytes('0x4c0c7077f770069785167e8b7451d63fad1e858ef5251b239eb0781c314000d2'), r=5665764915496639843348851536709769469640799172147461427941649091688603148258, s=48121517563314450138554207713326165180739298862566159641495137088718857193470, v=27, signature=HexBytes('0x0c86b594baa5bb06a0f4054ffdf3896377cfb757d42dcaeacf0241d96a4d5fe26a63d0514338ec600c89ee808dc088e7b3aadc55b9f5d86685d3ff212e2e47fe1b'))
When I pass these params such as signed_message.messageHash
and signed_message.signature
to the recoversigner(mentioned below) function in the smart contract. It gives the correct address. But when I am passing arguments tokens, contractaddress
(used above) to the solidity keccak256(abi.encodePacked(token,address(this)))
the message that is genarated is different from the signed_message.messageHash
and in turn I get a different address as output from ecrecover
.
I checked what is the output of keccak256(abi.encodePacked(..))
.
I found It is same as sig value
What I have done:
Make an ERC20 token.
setup the channel
My goal:
sign messages (with web3py)
validate the signed message with solidity.
Like this:
user will pass some arguments : like contract address and number of tokens along with the signed message. and I will validate it with
function splitSignature(bytes memory sig)
pure
internal
returns (uint8 v, bytes32 r, bytes32 s)
{
require(sig.length == 65 , "invalid length");
assembly {
r := mload(add(sig, 32))
s := mload(add(sig, 64))
v := byte(0, mload(add(sig, 96)))
}
if (v < 27) {
v += 27;
}
require(v == 27 || v == 28 , "value of v ");
return (v, r, s);
}
function recoverSigner(bytes32 message, bytes memory sig)
internal
pure
returns (address)
{
(uint8 v, bytes32 r, bytes32 s) = splitSignature(sig);
return ecrecover(message, v, r, s);
}
bytes32 message = prefixed(keccak256(abi.encodePacked(token,address(this))));
This is not working properly. I am not getting the correct address as output.
Please help..
I'm not entirely clear on what you're trying to do, but an important clarification to make here is that Web3's eth.sign
(and its underlying JSON-RPC call eth_sign
) do not sign a simple hash. It adds a prefix string, with a length embedded.
See the eth_sign
docs:
The sign method calculates an Ethereum specific signature with:
sign(keccak256("\x19Ethereum Signed Message:\n" + len(message) + message)))
.
By adding the prefix to the passed message, your client makes sure that you aren't using eth_sign
to sign a transaction (presumably, by accident, or due to attack).
Calling recoverHash(sig, ...)
is sort of nonsensical. Although the variable is called "sig
", it is just a hash of some data -- it hasn't been signed by anything.
If you are designing a new message signing scheme, I highly recommend using some kind of standard. If you want the simplest possible option, you could use EIP-191's "Version 0" message.
For that, you can use eth-account's encode_intended_validator()
, which would look something like:
from eth_account.messages import encode_intended_validator
message = encode_intended_validator(YOUR_CONTRACT_ADDR, sig)
signed_message = w3.eth.account.sign_message(message, private_key=private_key)