arnaucube
arnaucube/hermez-nodePublic Self-hosted repository
hermez-node/common/accountcreationauths.go
common/accountcreationauths.goRaw
  1package common
  2
  3import (
  4	"time"
  5
  6	ethCommon "github.com/ethereum/go-ethereum/common"
  7	ethMath "github.com/ethereum/go-ethereum/common/math"
  8	ethCrypto "github.com/ethereum/go-ethereum/crypto"
  9	ethSigner "github.com/ethereum/go-ethereum/signer/core"
 10	"github.com/hermeznetwork/tracerr"
 11	"github.com/iden3/go-iden3-crypto/babyjub"
 12)
 13
 14// AccountCreationAuthMsg is the message that is signed to authorize a Hermez
 15// account creation
 16const AccountCreationAuthMsg = "Account creation"
 17
 18// EIP712Version is the used version of the EIP-712
 19const EIP712Version = "1"
 20
 21// EIP712Provider defines the Provider for the EIP-712
 22const EIP712Provider = "Hermez Network"
 23
 24var (
 25	// EmptyEthSignature is an ethereum signature of all zeroes
 26	EmptyEthSignature = make([]byte, 65)
 27)
 28
 29// AccountCreationAuth authorizations sent by users to the L2DB, to be used for
 30// account creations when necessary
 31type AccountCreationAuth struct {
 32	EthAddr   ethCommon.Address     `meddler:"eth_addr"`
 33	BJJ       babyjub.PublicKeyComp `meddler:"bjj"`
 34	Signature []byte                `meddler:"signature"`
 35	Timestamp time.Time             `meddler:"timestamp,utctime"`
 36}
 37
 38// toHash returns a byte array to be hashed from the AccountCreationAuth, which
 39// follows the EIP-712 encoding
 40func (a *AccountCreationAuth) toHash(chainID uint16,
 41	hermezContractAddr ethCommon.Address) ([]byte, error) {
 42	chainIDFormatted := ethMath.NewHexOrDecimal256(int64(chainID))
 43
 44	signerData := ethSigner.TypedData{
 45		Types: ethSigner.Types{
 46			"EIP712Domain": []ethSigner.Type{
 47				{Name: "name", Type: "string"},
 48				{Name: "version", Type: "string"},
 49				{Name: "chainId", Type: "uint256"},
 50				{Name: "verifyingContract", Type: "address"},
 51			},
 52			"Authorise": []ethSigner.Type{
 53				{Name: "Provider", Type: "string"},
 54				{Name: "Authorisation", Type: "string"},
 55				{Name: "BJJKey", Type: "bytes32"},
 56			},
 57		},
 58		PrimaryType: "Authorise",
 59		Domain: ethSigner.TypedDataDomain{
 60			Name:              EIP712Provider,
 61			Version:           EIP712Version,
 62			ChainId:           chainIDFormatted,
 63			VerifyingContract: hermezContractAddr.Hex(),
 64		},
 65		Message: ethSigner.TypedDataMessage{
 66			"Provider":      EIP712Provider,
 67			"Authorisation": AccountCreationAuthMsg,
 68			"BJJKey":        SwapEndianness(a.BJJ[:]),
 69		},
 70	}
 71
 72	domainSeparator, err := signerData.HashStruct("EIP712Domain", signerData.Domain.Map())
 73	if err != nil {
 74		return nil, tracerr.Wrap(err)
 75	}
 76	typedDataHash, err := signerData.HashStruct(signerData.PrimaryType, signerData.Message)
 77	if err != nil {
 78		return nil, tracerr.Wrap(err)
 79	}
 80
 81	rawData := []byte{0x19, 0x01} // "\x19\x01"
 82	rawData = append(rawData, domainSeparator...)
 83	rawData = append(rawData, typedDataHash...)
 84	return rawData, nil
 85}
 86
 87// HashToSign returns the hash to be signed by the Ethereum address to authorize
 88// the account creation, which follows the EIP-712 encoding
 89func (a *AccountCreationAuth) HashToSign(chainID uint16,
 90	hermezContractAddr ethCommon.Address) ([]byte, error) {
 91	b, err := a.toHash(chainID, hermezContractAddr)
 92	if err != nil {
 93		return nil, tracerr.Wrap(err)
 94	}
 95	return ethCrypto.Keccak256(b), nil
 96}
 97
 98// Sign signs the account creation authorization message using the provided
 99// `signHash` function, and stores the signature in `a.Signature`.  `signHash`
100// should do an ethereum signature using the account corresponding to
101// `a.EthAddr`.  The `signHash` function is used to make signing flexible: in
102// tests we sign directly using the private key, outside tests we sign using
103// the keystore (which never exposes the private key). Sign follows the EIP-712
104// encoding.
105func (a *AccountCreationAuth) Sign(signHash func(hash []byte) ([]byte, error),
106	chainID uint16, hermezContractAddr ethCommon.Address) error {
107	hash, err := a.HashToSign(chainID, hermezContractAddr)
108	if err != nil {
109		return tracerr.Wrap(err)
110	}
111	sig, err := signHash(hash)
112	if err != nil {
113		return tracerr.Wrap(err)
114	}
115	sig[64] += 27
116	a.Signature = sig
117	a.Timestamp = time.Now()
118	return nil
119}
120
121// VerifySignature ensures that the Signature is done with the EthAddr, for the
122// chainID and hermezContractAddress passed by parameter. VerifySignature
123// follows the EIP-712 encoding.
124func (a *AccountCreationAuth) VerifySignature(chainID uint16,
125	hermezContractAddr ethCommon.Address) bool {
126	// Calculate hash to be signed
127	hash, err := a.HashToSign(chainID, hermezContractAddr)
128	if err != nil {
129		return false
130	}
131
132	var sig [65]byte
133	copy(sig[:], a.Signature[:])
134	sig[64] -= 27
135
136	// Get public key from Signature
137	pubKBytes, err := ethCrypto.Ecrecover(hash, sig[:])
138	if err != nil {
139		return false
140	}
141	pubK, err := ethCrypto.UnmarshalPubkey(pubKBytes)
142	if err != nil {
143		return false
144	}
145	// Get addr from pubK
146	addr := ethCrypto.PubkeyToAddress(*pubK)
147	return addr == a.EthAddr
148}