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}