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
 20// AccountCreationAuth authorizations sent by users to the L2DB, to be used for
 21// account creations when necessary
 22type AccountCreationAuth struct {
 23	EthAddr   ethCommon.Address     `meddler:"eth_addr"`
 24	BJJ       babyjub.PublicKeyComp `meddler:"bjj"`
 25	Signature []byte                `meddler:"signature"`
 26	Timestamp time.Time             `meddler:"timestamp,utctime"`
 27}
 28
 29func (a *AccountCreationAuth) toHash(chainID uint16,
 30	hermezContractAddr ethCommon.Address) []byte {
 31	var chainIDBytes [2]byte
 32	binary.BigEndian.PutUint16(chainIDBytes[:], chainID)
 33	// [EthPrefix | AccountCreationAuthMsg | compressedBJJ | chainID | hermezContractAddr]
 34	var b []byte
 35	b = append(b, []byte(AccountCreationAuthMsg)...)
 36	b = append(b, SwapEndianness(a.BJJ[:])...) // for js implementation compatibility
 37	b = append(b, chainIDBytes[:]...)
 38	b = append(b, hermezContractAddr[:]...)
 39
 40	ethPrefix := EthMsgPrefix + strconv.Itoa(len(b))
 41	return append([]byte(ethPrefix), b...)
 42}
 43
 44// HashToSign returns the hash to be signed by the Etherum address to authorize
 45// the account creation
 46func (a *AccountCreationAuth) HashToSign(chainID uint16,
 47	hermezContractAddr ethCommon.Address) ([]byte, error) {
 48	b := a.toHash(chainID, hermezContractAddr)
 49	return ethCrypto.Keccak256Hash(b).Bytes(), nil
 50}
 51
 52// Sign signs the account creation authorization message using the provided
 53// `signHash` function, and stores the signaure in `a.Signature`.  `signHash`
 54// should do an ethereum signature using the account corresponding to
 55// `a.EthAddr`.  The `signHash` function is used to make signig flexible: in
 56// tests we sign directly using the private key, outside tests we sign using
 57// the keystore (which never exposes the private key).
 58func (a *AccountCreationAuth) Sign(signHash func(hash []byte) ([]byte, error),
 59	chainID uint16, hermezContractAddr ethCommon.Address) error {
 60	hash, err := a.HashToSign(chainID, hermezContractAddr)
 61	if err != nil {
 62		return err
 63	}
 64	sig, err := signHash(hash)
 65	if err != nil {
 66		return err
 67	}
 68	sig[64] += 27
 69	a.Signature = sig
 70	a.Timestamp = time.Now()
 71	return nil
 72}
 73
 74// VerifySignature ensures that the Signature is done with the EthAddr, for the
 75// chainID and hermezContractAddress passed by parameter
 76func (a *AccountCreationAuth) VerifySignature(chainID uint16,
 77	hermezContractAddr ethCommon.Address) bool {
 78	// Calculate hash to be signed
 79	hash, err := a.HashToSign(chainID, hermezContractAddr)
 80	if err != nil {
 81		return false
 82	}
 83
 84	var sig [65]byte
 85	copy(sig[:], a.Signature[:])
 86	sig[64] -= 27
 87
 88	// Get public key from Signature
 89	pubKBytes, err := ethCrypto.Ecrecover(hash, sig[:])
 90	if err != nil {
 91		return false
 92	}
 93	pubK, err := ethCrypto.UnmarshalPubkey(pubKBytes)
 94	if err != nil {
 95		return false
 96	}
 97	// Get addr from pubK
 98	addr := ethCrypto.PubkeyToAddress(*pubK)
 99	return addr == a.EthAddr
100}