You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

100 lines
3.3 KiB

package common
import (
"encoding/binary"
"strconv"
"time"
ethCommon "github.com/ethereum/go-ethereum/common"
ethCrypto "github.com/ethereum/go-ethereum/crypto"
"github.com/iden3/go-iden3-crypto/babyjub"
)
// AccountCreationAuthMsg is the message that is signed to authorize a Hermez
// account creation
const AccountCreationAuthMsg = "I authorize this babyjubjub key for hermez rollup account creation"
// EthMsgPrefix is the prefix for message signing at the Ethereum ecosystem
const EthMsgPrefix = "\x19Ethereum Signed Message:\n"
// AccountCreationAuth authorizations sent by users to the L2DB, to be used for
// account creations when necessary
type AccountCreationAuth struct {
EthAddr ethCommon.Address `meddler:"eth_addr"`
BJJ babyjub.PublicKeyComp `meddler:"bjj"`
Signature []byte `meddler:"signature"`
Timestamp time.Time `meddler:"timestamp,utctime"`
}
func (a *AccountCreationAuth) toHash(chainID uint16,
hermezContractAddr ethCommon.Address) []byte {
var chainIDBytes [2]byte
binary.BigEndian.PutUint16(chainIDBytes[:], chainID)
// [EthPrefix | AccountCreationAuthMsg | compressedBJJ | chainID | hermezContractAddr]
var b []byte
b = append(b, []byte(AccountCreationAuthMsg)...)
b = append(b, SwapEndianness(a.BJJ[:])...) // for js implementation compatibility
b = append(b, chainIDBytes[:]...)
b = append(b, hermezContractAddr[:]...)
ethPrefix := EthMsgPrefix + strconv.Itoa(len(b))
return append([]byte(ethPrefix), b...)
}
// HashToSign returns the hash to be signed by the Etherum address to authorize
// the account creation
func (a *AccountCreationAuth) HashToSign(chainID uint16,
hermezContractAddr ethCommon.Address) ([]byte, error) {
b := a.toHash(chainID, hermezContractAddr)
return ethCrypto.Keccak256Hash(b).Bytes(), nil
}
// Sign signs the account creation authorization message using the provided
// `signHash` function, and stores the signaure in `a.Signature`. `signHash`
// should do an ethereum signature using the account corresponding to
// `a.EthAddr`. The `signHash` function is used to make signig flexible: in
// tests we sign directly using the private key, outside tests we sign using
// the keystore (which never exposes the private key).
func (a *AccountCreationAuth) Sign(signHash func(hash []byte) ([]byte, error),
chainID uint16, hermezContractAddr ethCommon.Address) error {
hash, err := a.HashToSign(chainID, hermezContractAddr)
if err != nil {
return err
}
sig, err := signHash(hash)
if err != nil {
return err
}
sig[64] += 27
a.Signature = sig
a.Timestamp = time.Now()
return nil
}
// VerifySignature ensures that the Signature is done with the EthAddr, for the
// chainID and hermezContractAddress passed by parameter
func (a *AccountCreationAuth) VerifySignature(chainID uint16,
hermezContractAddr ethCommon.Address) bool {
// Calculate hash to be signed
hash, err := a.HashToSign(chainID, hermezContractAddr)
if err != nil {
return false
}
var sig [65]byte
copy(sig[:], a.Signature[:])
sig[64] -= 27
// Get public key from Signature
pubKBytes, err := ethCrypto.Ecrecover(hash, sig[:])
if err != nil {
return false
}
pubK, err := ethCrypto.UnmarshalPubkey(pubKBytes)
if err != nil {
return false
}
// Get addr from pubK
addr := ethCrypto.PubkeyToAddress(*pubK)
return addr == a.EthAddr
}