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.

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