'sign and verify message with solidity and python
I am trying to perform a certain action say like selling a token only when the message sent to a contract is signed by the contract owner. I knew how I would do this in js, but I am having a hard time finding a clear documentation for python. Here is what I have at the moment.
my solidity
function buy_token(string memory tokenURI, bytes memory signature) payable public{
// Make sure this token uri is signed by the owner of the contract
// to the recipient that is asking to buy
address signer_address = recoverSigner(keccak256(abi.encodePacked(tokenURI, msg.sender)), signature);
require(signer_address == minter, "NFT not authorized by this contract");
require(msg.value >= cprice, "Insufficient funds to buy");
require(minter != msg.sender, "Cant sell to owner");
uint256 mint_id = mint(tokenURI);
// transfer the token to the buyer
_transfer(minter, msg.sender, mint_id);
}
// functions from documentatations
function recoverSigner(bytes32 message, bytes memory sig)
public
pure
returns (address)
{
uint8 v;
bytes32 r;
bytes32 s;
(v, r, s) = splitSignature(sig);
return ecrecover(message, v, r, s);
}
function splitSignature(bytes memory sig)
public
pure
returns (uint8, bytes32, bytes32)
{
require(sig.length == 65, 'Wrong byte lenght');
bytes32 r;
bytes32 s;
uint8 v;
assembly {
// first 32 bytes, after the length prefix
r := mload(add(sig, 32))
// second 32 bytes
s := mload(add(sig, 64))
// final byte (first byte of the next 32 bytes)
v := byte(0, mload(add(sig, 96)))
}
return (v, r, s);
}
and here is my python brownie code, which tries to emulate both a deployer and a user. The deployer signs a message and gives it to the user and the user tries to buy a token using the signed message.
from brownie import Contract, accounts
from web3.auto import w3
from eth_account.messages import encode_defunct
from eth_abi.packed import encode_abi_packed
from eth_utils import keccak
def main():
# This part is for test in the real program the smart
# contract would alredy be deployed
deployer = accounts.add()
Contract.deploy({
'from':deployer
})
msg = """JSON FORMATED STRING"""
hash = keccak(encode_abi_packed(['string','address'],[tokenURI, accounts[0].address]))
message = encode_defunct(text=hash.hex())
signed_message = w3.eth.account.sign_message(message, private_key=deployer.private_key)
signed = str(signed_message.messageHash.hex())
tx = Contract[0].buy_token(tokenURI, signed,{'from':accounts[0], 'value': Contract[0].cprice() + 10000000000})
print(tx.info, tx.events)
I have tried all the methods suggested in similar posts, but I couldn't get them to work, the current error is being raised by the following line.
require(sig.length == 65, 'Wrong byte lenght');
But all the methods I try generate a signed message with a length of 66.
Solution 1:[1]
Should be signed_message.signature.hex()
instead of signed_message.messageHash.hex()
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Damian G?dziak |