arnaucube
arnaucube/hermez-nodePublic Self-hosted repository
hermez-node/common/accountcreationauths.go
common/accountcreationauths.goRaw
  1package common
  2
  3import (
  4	"encoding/binary"
  5	"strconv"
  6	"time"
  7
  8	ethCommon "github.com/ethereum/go-ethereum/common"
  9	ethCrypto "github.com/ethereum/go-ethereum/crypto"
 10	"github.com/iden3/go-iden3-crypto/babyjub"
 11)
 12
 13// AccountCreationAuthMsg is the message that is signed to authorize a Hermez
 14// account creation
 15const AccountCreationAuthMsg = "I authorize this babyjubjub key for hermez rollup account creation"
 16
 17// EthMsgPrefix is the prefix for message signing at the Ethereum ecosystem
 18const EthMsgPrefix = "\x19Ethereum Signed Message:\n"
 19
 20var (
 21	// EmptyEthSignature is an ethereum signature of all zeroes
 22	EmptyEthSignature = make([]byte, 65)
 23)
 24
 25// AccountCreationAuth authorizations sent by users to the L2DB, to be used for
 26// account creations when necessary
 27type AccountCreationAuth struct {
 28	EthAddr   ethCommon.Address     `meddler:"eth_addr"`
 29	BJJ       babyjub.PublicKeyComp `meddler:"bjj"`
 30	Signature []byte                `meddler:"signature"`
 31	Timestamp time.Time             `meddler:"timestamp,utctime"`
 32}
 33
 34func (a *AccountCreationAuth) toHash(chainID uint16,
 35	hermezContractAddr ethCommon.Address) []byte {
 36	var chainIDBytes [2]byte
 37	binary.BigEndian.PutUint16(chainIDBytes[:], chainID)
 38	// [EthPrefix | AccountCreationAuthMsg | compressedBJJ | chainID | hermezContractAddr]
 39	var b []byte
 40	b = append(b, []byte(AccountCreationAuthMsg)...)
 41	b = append(b, SwapEndianness(a.BJJ[:])...) // for js implementation compatibility
 42	b = append(b, chainIDBytes[:]...)
 43	b = append(b, hermezContractAddr[:]...)
 44
 45	ethPrefix := EthMsgPrefix + strconv.Itoa(len(b))
 46	return append([]byte(ethPrefix), b...)
 47}
 48
 49// HashToSign returns the hash to be signed by the Etherum address to authorize
 50// the account creation
 51func (a *AccountCreationAuth) HashToSign(chainID uint16,
 52	hermezContractAddr ethCommon.Address) ([]byte, error) {
 53	b := a.toHash(chainID, hermezContractAddr)
 54	return ethCrypto.Keccak256Hash(b).Bytes(), nil
 55}
 56
 57// Sign signs the account creation authorization message using the provided
 58// `signHash` function, and stores the signaure in `a.Signature`.  `signHash`
 59// should do an ethereum signature using the account corresponding to
 60// `a.EthAddr`.  The `signHash` function is used to make signig flexible: in
 61// tests we sign directly using the private key, outside tests we sign using
 62// the keystore (which never exposes the private key).
 63func (a *AccountCreationAuth) Sign(signHash func(hash []byte) ([]byte, error),
 64	chainID uint16, hermezContractAddr ethCommon.Address) error {
 65	hash, err := a.HashToSign(chainID, hermezContractAddr)
 66	if err != nil {
 67		return err
 68	}
 69	sig, err := signHash(hash)
 70	if err != nil {
 71		return err
 72	}
 73	sig[64] += 27
 74	a.Signature = sig
 75	a.Timestamp = time.Now()
 76	return nil
 77}
 78
 79// VerifySignature ensures that the Signature is done with the EthAddr, for the
 80// chainID and hermezContractAddress passed by parameter
 81func (a *AccountCreationAuth) VerifySignature(chainID uint16,
 82	hermezContractAddr ethCommon.Address) bool {
 83	// Calculate hash to be signed
 84	hash, err := a.HashToSign(chainID, hermezContractAddr)
 85	if err != nil {
 86		return false
 87	}
 88
 89	var sig [65]byte
 90	copy(sig[:], a.Signature[:])
 91	sig[64] -= 27
 92
 93	// Get public key from Signature
 94	pubKBytes, err := ethCrypto.Ecrecover(hash, sig[:])
 95	if err != nil {
 96		return false
 97	}
 98	pubK, err := ethCrypto.UnmarshalPubkey(pubKBytes)
 99	if err != nil {
100		return false
101	}
102	// Get addr from pubK
103	addr := ethCrypto.PubkeyToAddress(*pubK)
104	return addr == a.EthAddr
105}