|
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"
|
|
|
|
var (
|
|
// EmptyEthSignature is an ethereum signature of all zeroes
|
|
EmptyEthSignature = make([]byte, 65)
|
|
)
|
|
|
|
// 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
|
|
}
|