You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

105 lines
3.4 KiB

  1. package common
  2. import (
  3. "encoding/binary"
  4. "strconv"
  5. "time"
  6. ethCommon "github.com/ethereum/go-ethereum/common"
  7. ethCrypto "github.com/ethereum/go-ethereum/crypto"
  8. "github.com/iden3/go-iden3-crypto/babyjub"
  9. )
  10. // AccountCreationAuthMsg is the message that is signed to authorize a Hermez
  11. // account creation
  12. const AccountCreationAuthMsg = "I authorize this babyjubjub key for hermez rollup account creation"
  13. // EthMsgPrefix is the prefix for message signing at the Ethereum ecosystem
  14. const EthMsgPrefix = "\x19Ethereum Signed Message:\n"
  15. var (
  16. // EmptyEthSignature is an ethereum signature of all zeroes
  17. EmptyEthSignature = make([]byte, 65)
  18. )
  19. // AccountCreationAuth authorizations sent by users to the L2DB, to be used for
  20. // account creations when necessary
  21. type AccountCreationAuth struct {
  22. EthAddr ethCommon.Address `meddler:"eth_addr"`
  23. BJJ babyjub.PublicKeyComp `meddler:"bjj"`
  24. Signature []byte `meddler:"signature"`
  25. Timestamp time.Time `meddler:"timestamp,utctime"`
  26. }
  27. func (a *AccountCreationAuth) toHash(chainID uint16,
  28. hermezContractAddr ethCommon.Address) []byte {
  29. var chainIDBytes [2]byte
  30. binary.BigEndian.PutUint16(chainIDBytes[:], chainID)
  31. // [EthPrefix | AccountCreationAuthMsg | compressedBJJ | chainID | hermezContractAddr]
  32. var b []byte
  33. b = append(b, []byte(AccountCreationAuthMsg)...)
  34. b = append(b, SwapEndianness(a.BJJ[:])...) // for js implementation compatibility
  35. b = append(b, chainIDBytes[:]...)
  36. b = append(b, hermezContractAddr[:]...)
  37. ethPrefix := EthMsgPrefix + strconv.Itoa(len(b))
  38. return append([]byte(ethPrefix), b...)
  39. }
  40. // HashToSign returns the hash to be signed by the Etherum address to authorize
  41. // the account creation
  42. func (a *AccountCreationAuth) HashToSign(chainID uint16,
  43. hermezContractAddr ethCommon.Address) ([]byte, error) {
  44. b := a.toHash(chainID, hermezContractAddr)
  45. return ethCrypto.Keccak256Hash(b).Bytes(), nil
  46. }
  47. // Sign signs the account creation authorization message using the provided
  48. // `signHash` function, and stores the signaure in `a.Signature`. `signHash`
  49. // should do an ethereum signature using the account corresponding to
  50. // `a.EthAddr`. The `signHash` function is used to make signig flexible: in
  51. // tests we sign directly using the private key, outside tests we sign using
  52. // the keystore (which never exposes the private key).
  53. func (a *AccountCreationAuth) Sign(signHash func(hash []byte) ([]byte, error),
  54. chainID uint16, hermezContractAddr ethCommon.Address) error {
  55. hash, err := a.HashToSign(chainID, hermezContractAddr)
  56. if err != nil {
  57. return err
  58. }
  59. sig, err := signHash(hash)
  60. if err != nil {
  61. return err
  62. }
  63. sig[64] += 27
  64. a.Signature = sig
  65. a.Timestamp = time.Now()
  66. return nil
  67. }
  68. // VerifySignature ensures that the Signature is done with the EthAddr, for the
  69. // chainID and hermezContractAddress passed by parameter
  70. func (a *AccountCreationAuth) VerifySignature(chainID uint16,
  71. hermezContractAddr ethCommon.Address) bool {
  72. // Calculate hash to be signed
  73. hash, err := a.HashToSign(chainID, hermezContractAddr)
  74. if err != nil {
  75. return false
  76. }
  77. var sig [65]byte
  78. copy(sig[:], a.Signature[:])
  79. sig[64] -= 27
  80. // Get public key from Signature
  81. pubKBytes, err := ethCrypto.Ecrecover(hash, sig[:])
  82. if err != nil {
  83. return false
  84. }
  85. pubK, err := ethCrypto.UnmarshalPubkey(pubKBytes)
  86. if err != nil {
  87. return false
  88. }
  89. // Get addr from pubK
  90. addr := ethCrypto.PubkeyToAddress(*pubK)
  91. return addr == a.EthAddr
  92. }