Extract Data from Signed Message in Solidity – How to Guide

dapp-developmenthashSecuritysignature

In an effort to reduce gas costs for users, I'm looking for a way to delegate certain transactions to a contract owner so that the owner makes a transactions that would have been otherwise sent by the user. So instead of sending a transaction directly to the blockchain, the user could sign a message in MetaMask and send the message as an authorization to the server, which would then send a transaction to the blockchain, assuming other conditions are met.

However, to provide maximum assurance to users that only authorized transactions are processed/sent by the contract owner, I'd like to be able to unpack the signed message within the contract so that the following are verified:

1) The user's address

2) The transactions data

At a high level, the solidity function would look something like this.

function processDelegatedTransaction(
    address userAddress,
    bytes32 msgHash,
    uint8 v,
    bytes32 r,
    bytes32 s,
    uint16 param1,
    uint16 param2
) public requireOwner {

    require(userAddress == ecrecover(msgHash, v, r, s));

    // Do something else to verify that param1 and param2 are packed in msgHash
}

The require statement should take care of #1 above, but #2 is the issue.

The message signing process could encode multiple parameters into a single uint, which would be included in the hash and then, if I'm able to unpack the hash, decode them similarly to the method described here:

https://medium.com/@novablitz/storing-structs-is-costing-you-gas-774da988895e

So the only sticking point is being able to extract the params from the hash or recreate the hash from the params. Is this possible?

Best Answer

The typical solution here is to not accept a hash at all but just recreate it. Something like this:

function processDelegatedTransaction(
    address userAddress,
    uint8 v,
    bytes32 r,
    bytes32 s,
    uint16 param1,
    uint16 param2
) public requireOwner {
    bytes32 msgHash = keccak256(abi.encodePacked(param1, param2));
    require(userAddress == ecrecover(msgHash, v, r, s));
}

Depending on how you're producing the signature in the first place, you may need to do either EIP712 or prefixed (geth/Parity-style) messages. For the latter, see this example:

https://programtheblockchain.com/posts/2018/02/17/signing-and-verifying-messages-in-ethereum/

Related Topic