0

I am passing 64 byte public keys into my solidity function as two 32 byte parts, high 32 bytes and low 32 bytes. How do I combine them back to a 64 byte value in Solidity (cost efficiently)? Looping and adding byte by byte would be quite expensive, but I suspect there should be a solution that involves loading values into memory and reading them from there using assembly, but so far I can't figure out how to.

function generateAddress(bytes32 pubKeyHigh, bytes32 pubKeyLow) returns (address) {
    bytes pubKey = ..... combine pubKeyHigh and pubKeyLow ..... 
    return address(keccak256(pubKey));
}
  • The efficient solution would be to pass the 64 bytes word instead of two of 32 bytes – Jaime Jul 13 '18 at 11:38
  • I do need to pass two bytes32 because further in the code i need to store the pub key. And storing two bytes32 numbers is about 25% cheaper than storing a bytes value – Andrey Jul 13 '18 at 11:55
1

I figured it out. I just need to use abi.encode(pubKeyHigh, pubKeyLow) to get back bytes value that is the full 64 byte public key.

So, rewriting my original function:

function generateAddress(bytes32 pubKeyHigh, bytes32 pubKeyLow) public pure returns (address) {
    bytes pubKey = abi.encode(pubKeyHigh, pubKeyLow); 
    return address(keccak256(pubKey));
}
  • Why do you need to pass high and low separately and not as a single argument? 🤔 – schemar Jul 14 '18 at 20:32
  • @Schemar - because i want to store the public key as two bytes32 values vs one bytes value - it's about 25% cheaper. – Andrey Jul 14 '18 at 21:15
  • Doesn’t abi.encode() make it a single value? – schemar Jul 14 '18 at 21:20
  • Ah Right. Now I get it. Thank you for the explanation. – schemar Jul 14 '18 at 21:20
  • It does. So, I'm passing two values that i will be storing. But, i also need to validate them before storing them - i need to combine them to generate address and compare to the sender's address, to make sure they pass correct values. – Andrey Jul 14 '18 at 21:21
0

Ethereum has a 32 Byte word limit, you simply can't do it.

It's too large, you need to convert it on the client and store the converted value similar to the way IPFS hashes are too long to store in a bytes32 value , so we convert them ON THE CLIENT.

Example:

// Return bytes32 hex string from base58 encoded ipfs hash,
// stripping leading 2 bytes from 34 byte IPFS hash
// Assume IPFS defaults: function:0x12=sha2, size:0x20=256 bits
// E.g. "QmNSUYVKDSvPUnRLKmuxk9diJ6yS96r1TrAXzjTiBcCLAL" -->
// "0x017dfd85d4f6cb4dcd715a88101f7b1f06cd1e009b2327a0809d01eb9c91f231"
function bytes32FromIpfs(ipfsHash) {
  return (
    "0x" +
    bs58
      .decode(ipfsHash)
      .slice(2)
      .toString("hex")
  )
}

// Return base58 encoded ipfs hash from bytes32 hex string,
// E.g. "0x017dfd85d4f6cb4dcd715a88101f7b1f06cd1e009b2327a0809d01eb9c91f231"
// --> "QmNSUYVKDSvPUnRLKmuxk9diJ6yS96r1TrAXzjTiBcCLAL"
function ipfsFromBytes32(bytes32) {
  // Add our default ipfs values for first 2 bytes:
  // function:0x12=sha2, size:0x20=256 bits
  // and cut off leading "0x"
  const hashHex = "1220" + bytes32.slice(2)
  const hashBytes = Buffer.from(hashHex, "hex")
  const hashStr = bs58.encode(hashBytes)
  return hashStr
}
  • Wrong answer, sorry. Solidity has 32 byte word limit, but 64 bytes can be stored in bytes variable. Also, I figured out how to combine two bytes32 values back to 64 byte value, I'll post that in my answer – Andrey Jul 13 '18 at 14:04
  • Doing it in solidity would be wrong due to the inefficiency of dynamic types. – Nico Vergauwen Jul 13 '18 at 14:13
  • It depends on a task, right? I need to verify, in my smart contract, that the user sends the correct public key that corresponds to their sender address. So i need to reconstruct the private key from the pair, generate the address, and compare with sender. The example i use here is not the one from my code, it's greatly simplified just to ask the question on conversion. – Andrey Jul 13 '18 at 14:17
  • That would be the wrong way to do it, especially because you don't want to process privkeys in a contract, ever. Privatekeys should only be exposed on the client side and never leave a user's client. take the last 20 bytes of the keccak hash of the pubkey and you'll get the address, you can compare that with msg.sender – Nico Vergauwen Jul 13 '18 at 14:22
  • Casting keccak to address type does the trick - address casting takes 20 lower bytes. I've tested it, it works. – Andrey Jul 13 '18 at 14:23

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.