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
14const (
15 // AccountCreationAuthMsg is the message that is signed to authorize a
16 // Hermez account creation
17 AccountCreationAuthMsg = "Account creation"
18 // EIP712Version is the used version of the EIP-712
19 EIP712Version = "1"
20 // EIP712Provider defines the Provider for the EIP-712
21 EIP712Provider = "Hermez Network"
22)
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}