Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I get the same return value as solidity `abi.encodePacked` in Golang

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));
    }
}
like image 722
chengbin du Avatar asked Jun 09 '18 09:06

chengbin du


2 Answers

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
}
like image 194
chengbin du Avatar answered Sep 20 '22 22:09

chengbin du


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.

like image 30
Magomed Abdurakhmanov Avatar answered Sep 18 '22 22:09

Magomed Abdurakhmanov