6

How can I get the public key of msg.sender from inside the smart contract?

function getPublicKey() returns (string publicKey) {}

From Solidity docs, ecrecover returns an address:

ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns
(address): recover the address associated with the public key from
elliptic curve signature or return zero on error

From ethereumjs-util library, ecrecover returns a public key:

/**
 * ECDSA public key recovery from signature
 * @param {Buffer} msgHash
 * @param {Number} v
 * @param {Buffer} r
 * @param {Buffer} s
 * @return {Buffer} publicKey
 */
exports.ecrecover = function (msgHash, v, r, s) {
  var signature = Buffer.concat([exports.setLength(r, 32), exports.setLength(s, 32)], 64)
  var recovery = v - 27
  if (recovery !== 0 && recovery !== 1) {
    throw new Error('Invalid signature v value')
  }
  var senderPubKey = secp256k1.recover(msgHash, signature, recovery)
  return secp256k1.publicKeyConvert(senderPubKey, false).slice(1)
}

I assume, that when the transaction is sent, there is all information necessary to derive a public key for the sender's address, but I'm struggling to find any clear documentation on how to do it.


My current approach is:

function getPublicKey(account) {
  return new Promise((resolve, reject) => {
    web3.eth.sendTransaction({
      from: account,
      to: account,
      value: 0,
    }, (err, txId) => {
      if (err) return reject(err)
      web3.eth.getTransaction(txId, (err, tx) => {
        if (err) return reject(err)
        return resolve(tx.publicKey)
      })
    })
  })
}
  • 2
    I don't believe that contracts can access the public key of the sender, since the raw transaction data is not available to it. Given the public key, however, it would be easy to verify that it matches the address – Tjaden Hess Apr 12 '17 at 12:46
  • I've added my current implementation to get public key of the account, which is further saved in a smart contract. But I want to at least verify (on-chain) that it's indeed the right public key of msg.sender. @TjadenHess could you advice how this could be done? – Edward Ruchevits Apr 13 '17 at 10:02
6

Contracts cannot access the public key of the sender, since the raw transaction data is not visible to contracts. However, given the public key, you can verify that it does in fact match the address of the sender by re-deriving the address from the public key.

In Solidity it would look something like this:

function checkPubKey(bytes pubkey) constant returns (bool){
    return (uint(keccak256(pubkey)) & 0x00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) == uint(msg.sender);
}
  • 1
    Error: Operator & not compatible with types bytes32 and address\n return (keccak256(publicKey) & 0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF) == uint(addr); – Edward Ruchevits Apr 13 '17 at 14:54
  • Error: Operator == not compatible with types bytes32 and uint256\n return (keccak256(publicKey) & 0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF) == uint(addr);\n – Edward Ruchevits Apr 13 '17 at 14:55
  • 3
    Oh yeah, sorry just had to cast the type – Tjaden Hess Apr 13 '17 at 14:55
  • 1
    Wow, that was a quick edit! – Edward Ruchevits Apr 13 '17 at 14:57

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.