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}