10

How can i run abi.encodePacked in golang?

in solidity i use keccak256(abi.encodePacked(a, b)) to calc the signature of the params.

here is my contract.

pragma solidity ^0.4.24;

import "openzeppelin-solidity/contracts/ECRecovery.sol";


contract MyContract {
    using ECRecovery for bytes32;
    address permittedSinger;

    function doSomething(
    bytes32 id, uint256 amount, bytes sig
    ) {
        bytes32 hash = getHash(msg.sender, id, amount);
        address msgSigner = hash.recover(sig);
        require(msgSigner == permittedSinger);
    }

    function getMsgSigner(bytes32 proveHash, bytes sig) public pure returns (address) {
        return proveHash.recover(sig);
    }

    function getHash(
    address receiver, bytes32 id, uint256 amount
    ) pure returns (bytes32) {
        return keccak256(abi.encodePacked(receiver, id, amount));
    }
}
chengbin du
  • 512
  • 4
  • 13
  • No, I need to build some params in Golang. If someone call the contract, I want to check these params are valid. So i have to calculate the signature of the params in Golang, then check it in Contract. – chengbin du Jun 09 '18 at 09:39
  • look here [go-ethereum source code](https://github.com/ethereum/go-ethereum/blob/933972d139b0fe291cb01c7ad2c0f9d3109a68dd/accounts/abi/abi_test.go#L212) – Chiheb Nexus Jun 09 '18 at 10:36
  • 1
    The easiest thing to do would just be to call the smart contract's `getHash` from your Go code. – user94559 Jun 09 '18 at 15:41
  • 1
    @ChihebNexus thanks – chengbin du Jun 09 '18 at 15:44

2 Answers2

10

finally I managed to do it. :)

package main

import (
    "math/big"
    "github.com/ethereum/go-ethereum/common/hexutil"
    "github.com/ethereum/go-ethereum/accounts/abi"
    "log"
    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/crypto/sha3"
)

func main() {
    uint256Ty, _ := abi.NewType("uint256")
    bytes32Ty, _ := abi.NewType("bytes32")
    addressTy, _ := abi.NewType("address")

    arguments := abi.Arguments{
        {
            Type: addressTy,
        },
        {
            Type: bytes32Ty,
        },
        {
            Type: uint256Ty,
        },
    }

    bytes, _ := arguments.Pack(
        common.HexToAddress("0x0000000000000000000000000000000000000000"),
        [32]byte{'I','D','1'},
        big.NewInt(42),
    )

    var buf []byte
    hash := sha3.NewKeccak256()
    hash.Write(bytes)
    buf = hash.Sum(buf)

    log.Println(hexutil.Encode(buf))
    // output:
    // 0x1f214438d7c061ad56f98540db9a082d372df1ba9a3c96367f0103aa16c2fe9a
}
chengbin du
  • 512
  • 4
  • 13
  • 1
    Unfortunately, it doesn't work the same for dynamic length types (like `bytes`). Go's `arguments.Pack` will return what you get from `abi.encode` not `abi.encodePacked`. – Kuba Feb 27 '19 at 19:37
  • 2
    go-ethereum 1.9.7 has a few more args in `abi.NewType`. The above still work with ```abi.NewType("uint256", "uint256", nil)``` – Justin Nov 14 '19 at 17:04
  • what if an array like two addresses? – anonymous Jun 28 '21 at 17:57
5

As Jakub N is said in comments to accepted answer, Go's arguments.Pack returns as abi.encode and not abi.encodePacked. In your case it works because all packed values are 32 bytes, but if you also add some strings then the result will be different.

Here is how to do it to be compatible with tightly packed encoding corresponding to abi.encodePacked:


// hash of packed byte array with arguments

hash := crypto.Keccak256Hash(
        common.HexToAddress("0x0000000000000000000000000000000000000000").Bytes(),
        [32]byte{'I','D','1'},
        common.LeftPadBytes(big.NewInt(42).Bytes(), 32),
        []byte("Some other string value"),
    )

// normally we sign prefixed hash
// as in solidity with `ECDSA.toEthSignedMessageHash`

prefixedHash := crypto.Keccak256Hash(
        []byte(fmt.Sprintf("\x19Ethereum Signed Message:\n%v", len(hash))),
        hash.Bytes(),
    )

// sign hash to validate later in Solidity

sig, err := crypto.Sign(prefixedHash.Bytes(), privateKey)

It is also more efficient as we don't pack and allocate additional memory for that. Hash function iterates over existing values.

Magomed Abdurakhmanov
  • 1,814
  • 1
  • 16
  • 14