@ -1,21 +1,25 @@
package common
package common
import (
import (
"encoding/binary"
"strconv"
"time"
"time"
ethCommon "github.com/ethereum/go-ethereum/common"
ethCommon "github.com/ethereum/go-ethereum/common"
ethMath "github.com/ethereum/go-ethereum/common/math"
ethCrypto "github.com/ethereum/go-ethereum/crypto"
ethCrypto "github.com/ethereum/go-ethereum/crypto"
ethSigner "github.com/ethereum/go-ethereum/signer/core"
"github.com/hermeznetwork/tracerr"
"github.com/iden3/go-iden3-crypto/babyjub"
"github.com/iden3/go-iden3-crypto/babyjub"
)
)
// AccountCreationAuthMsg is the message that is signed to authorize a Hermez
// AccountCreationAuthMsg is the message that is signed to authorize a Hermez
// account creation
// account creation
const AccountCreationAuthMsg = "I authorize this babyjubjub key for hermez rollup a ccount creation"
const AccountCreationAuthMsg = "A ccount creation"
// EthMsgPrefix is the prefix for message signing at the Ethereum ecosystem
const EthMsgPrefix = "\x19Ethereum Signed Message:\n"
// EIP712Version is the used version of the EIP-712
const EIP712Version = "1"
// EIP712Provider defines the Provider for the EIP-712
const EIP712Provider = "Hermez Network"
var (
var (
// EmptyEthSignature is an ethereum signature of all zeroes
// EmptyEthSignature is an ethereum signature of all zeroes
@ -31,27 +35,64 @@ type AccountCreationAuth struct {
Timestamp time . Time ` meddler:"timestamp,utctime" `
Timestamp time . Time ` meddler:"timestamp,utctime" `
}
}
// toHash returns a byte array to be hashed from the AccountCreationAuth, which
// follows the EIP-712 encoding
func ( a * AccountCreationAuth ) toHash ( chainID uint16 ,
func ( a * AccountCreationAuth ) toHash ( chainID uint16 ,
hermezContractAddr ethCommon . Address ) [ ] byte {
var chainIDBytes [ 2 ] byte
binary . BigEndian . PutUint16 ( chainIDBytes [ : ] , chainID )
// [EthPrefix | AccountCreationAuthMsg | compressedBJJ | chainID | hermezContractAddr]
var b [ ] byte
b = append ( b , [ ] byte ( AccountCreationAuthMsg ) ... )
b = append ( b , SwapEndianness ( a . BJJ [ : ] ) ... ) // for js implementation compatibility
b = append ( b , chainIDBytes [ : ] ... )
b = append ( b , hermezContractAddr [ : ] ... )
ethPrefix := EthMsgPrefix + strconv . Itoa ( len ( b ) )
return append ( [ ] byte ( ethPrefix ) , b ... )
hermezContractAddr ethCommon . Address ) ( [ ] byte , error ) {
chainIDFormatted := ethMath . NewHexOrDecimal256 ( int64 ( chainID ) )
signerData := ethSigner . TypedData {
Types : ethSigner . Types {
"EIP712Domain" : [ ] ethSigner . Type {
{ Name : "name" , Type : "string" } ,
{ Name : "version" , Type : "string" } ,
{ Name : "chainId" , Type : "uint256" } ,
{ Name : "verifyingContract" , Type : "address" } ,
} ,
"Authorise" : [ ] ethSigner . Type {
{ Name : "Provider" , Type : "string" } ,
{ Name : "Authorisation" , Type : "string" } ,
{ Name : "BJJKey" , Type : "bytes32" } ,
} ,
} ,
PrimaryType : "Authorise" ,
Domain : ethSigner . TypedDataDomain {
Name : EIP712Provider ,
Version : EIP712Version ,
ChainId : chainIDFormatted ,
VerifyingContract : hermezContractAddr . Hex ( ) ,
} ,
Message : ethSigner . TypedDataMessage {
"Provider" : EIP712Provider ,
"Authorisation" : AccountCreationAuthMsg ,
"BJJKey" : SwapEndianness ( a . BJJ [ : ] ) ,
} ,
}
domainSeparator , err := signerData . HashStruct ( "EIP712Domain" , signerData . Domain . Map ( ) )
if err != nil {
return nil , tracerr . Wrap ( err )
}
typedDataHash , err := signerData . HashStruct ( signerData . PrimaryType , signerData . Message )
if err != nil {
return nil , tracerr . Wrap ( err )
}
rawData := [ ] byte { 0x19 , 0x01 } // "\x19\x01"
rawData = append ( rawData , domainSeparator ... )
rawData = append ( rawData , typedDataHash ... )
return rawData , nil
}
}
// HashToSign returns the hash to be signed by the Etherum address to authorize
// HashToSign returns the hash to be signed by the Etherum address to authorize
// the account creation
// the account creation, which follows the EIP-712 encoding
func ( a * AccountCreationAuth ) HashToSign ( chainID uint16 ,
func ( a * AccountCreationAuth ) HashToSign ( chainID uint16 ,
hermezContractAddr ethCommon . Address ) ( [ ] byte , error ) {
hermezContractAddr ethCommon . Address ) ( [ ] byte , error ) {
b := a . toHash ( chainID , hermezContractAddr )
return ethCrypto . Keccak256Hash ( b ) . Bytes ( ) , nil
b , err := a . toHash ( chainID , hermezContractAddr )
if err != nil {
return nil , tracerr . Wrap ( err )
}
return ethCrypto . Keccak256 ( b ) , nil
}
}
// Sign signs the account creation authorization message using the provided
// Sign signs the account creation authorization message using the provided
@ -59,16 +100,17 @@ func (a *AccountCreationAuth) HashToSign(chainID uint16,
// should do an ethereum signature using the account corresponding to
// should do an ethereum signature using the account corresponding to
// `a.EthAddr`. The `signHash` function is used to make signig flexible: in
// `a.EthAddr`. The `signHash` function is used to make signig flexible: in
// tests we sign directly using the private key, outside tests we sign using
// tests we sign directly using the private key, outside tests we sign using
// the keystore (which never exposes the private key).
// the keystore (which never exposes the private key). Sign follows the EIP-712
// encoding.
func ( a * AccountCreationAuth ) Sign ( signHash func ( hash [ ] byte ) ( [ ] byte , error ) ,
func ( a * AccountCreationAuth ) Sign ( signHash func ( hash [ ] byte ) ( [ ] byte , error ) ,
chainID uint16 , hermezContractAddr ethCommon . Address ) error {
chainID uint16 , hermezContractAddr ethCommon . Address ) error {
hash , err := a . HashToSign ( chainID , hermezContractAddr )
hash , err := a . HashToSign ( chainID , hermezContractAddr )
if err != nil {
if err != nil {
return err
return trac err. Wrap ( err )
}
}
sig , err := signHash ( hash )
sig , err := signHash ( hash )
if err != nil {
if err != nil {
return err
return trac err. Wrap ( err )
}
}
sig [ 64 ] += 27
sig [ 64 ] += 27
a . Signature = sig
a . Signature = sig
@ -77,7 +119,8 @@ func (a *AccountCreationAuth) Sign(signHash func(hash []byte) ([]byte, error),
}
}
// VerifySignature ensures that the Signature is done with the EthAddr, for the
// VerifySignature ensures that the Signature is done with the EthAddr, for the
// chainID and hermezContractAddress passed by parameter
// chainID and hermezContractAddress passed by parameter. VerifySignature
// follows the EIP-712 encoding.
func ( a * AccountCreationAuth ) VerifySignature ( chainID uint16 ,
func ( a * AccountCreationAuth ) VerifySignature ( chainID uint16 ,
hermezContractAddr ethCommon . Address ) bool {
hermezContractAddr ethCommon . Address ) bool {
// Calculate hash to be signed
// Calculate hash to be signed