Merge pull request #416 from hermeznetwork/feature/update-accountcreationauth Update AccountCreationAuth & fix auth.HashToSign
@@ -21,7 +21,7 @@ return
} // API to common + verify signature commonAuth := accountCreationAuthAPIToCommon(&apiAuth) - if !commonAuth.VerifySignature() { + if !commonAuth.VerifySignature(a.chainID, a.hermezAddress) { retBadReq(errors.New("invalid signature"), c) return }
@@ -4,6 +4,7 @@ import (
"errors" "sync" + ethCommon "github.com/ethereum/go-ethereum/common" "github.com/gin-gonic/gin" "github.com/hermeznetwork/hermez-node/common" "github.com/hermeznetwork/hermez-node/db/historydb"@@ -31,12 +32,13 @@ }
// API serves HTTP requests to allow external interaction with the Hermez node type API struct { - h *historydb.HistoryDB - cg *configAPI - s *statedb.StateDB - l2 *l2db.L2DB - status Status - chainID uint16 + h *historydb.HistoryDB + cg *configAPI + s *statedb.StateDB + l2 *l2db.L2DB + status Status + chainID uint16 + hermezAddress ethCommon.Address } // NewAPI sets the endpoints and the appropriate handlers, but doesn't start the server@@ -47,7 +49,6 @@ hdb *historydb.HistoryDB,
sdb *statedb.StateDB, l2db *l2db.L2DB, config *Config, - chainID uint16, ) (*API, error) { // Check input // TODO: is stateDB only needed for explorer endpoints or for both?@@ -65,10 +66,11 @@ RollupConstants: *newRollupConstants(config.RollupConstants),
AuctionConstants: config.AuctionConstants, WDelayerConstants: config.WDelayerConstants, }, - s: sdb, - l2: l2db, - status: Status{}, - chainID: chainID, + s: sdb, + l2: l2db, + status: Status{}, + chainID: config.ChainID, + hermezAddress: config.HermezAddress, } // Add coordinator endpoints
@@ -221,7 +221,7 @@ l2DB := l2db.NewL2DB(database, 10, 100, 24*time.Hour)
test.WipeDB(l2DB.DB()) // this will clean HistoryDB and L2DB // Config (smart contract constants) - _config := getConfigTest() + _config := getConfigTest(chainID) config = configAPI{ RollupConstants: *newRollupConstants(_config.RollupConstants), AuctionConstants: _config.AuctionConstants,@@ -238,7 +238,6 @@ hdb,
sdb, l2DB, &_config, - chainID, ) if err != nil { panic(err)@@ -423,7 +422,7 @@ txs: testTxs,
exits: testExits, poolTxsToSend: poolTxsToSend, poolTxsToReceive: poolTxsToReceive, - auths: genTestAuths(test.GenAuths(5)), + auths: genTestAuths(test.GenAuths(5, _config.ChainID, _config.HermezAddress)), router: router, bids: testBids, slots: api.genTestSlots(
@@ -4,6 +4,7 @@ import (
"math/big" "net/http" + ethCommon "github.com/ethereum/go-ethereum/common" "github.com/gin-gonic/gin" "github.com/hermeznetwork/hermez-node/common" )@@ -51,6 +52,8 @@ type Config struct {
RollupConstants common.RollupConstants AuctionConstants common.AuctionConstants WDelayerConstants common.WDelayerConstants + ChainID uint16 + HermezAddress ethCommon.Address } type configAPI struct {
@@ -9,7 +9,7 @@ "github.com/hermeznetwork/hermez-node/common"
"github.com/stretchr/testify/assert" ) -func getConfigTest() Config { +func getConfigTest(chainID uint16) Config { var config Config var rollupPublicConstants common.RollupConstants@@ -39,6 +39,9 @@
config.RollupConstants = rollupPublicConstants config.AuctionConstants = auctionConstants config.WDelayerConstants = wdelayerConstants + + config.ChainID = chainID + config.HermezAddress = ethCommon.HexToAddress("0xc344E203a046Da13b0B4467EB7B3629D0C99F6E6") return config }
@@ -1,15 +1,20 @@
package common import ( + "encoding/binary" "time" ethCommon "github.com/ethereum/go-ethereum/common" ethCrypto "github.com/ethereum/go-ethereum/crypto" - "github.com/hermeznetwork/tracerr" "github.com/iden3/go-iden3-crypto/babyjub" ) -// AccountCreationAuth authorizations sent by users to the L2DB, to be used for account creations when necessary +// AccountCreationAuthMsg is the message that is signed to authorize an account +// creation +const AccountCreationAuthMsg = "I authorize this babyjubjub key for hermez rollup account creation" + +// AccountCreationAuth authorizations sent by users to the L2DB, to be used for +// account creations when necessary type AccountCreationAuth struct { EthAddr ethCommon.Address `meddler:"eth_addr"` BJJ babyjub.PublicKeyComp `meddler:"bjj"`@@ -18,21 +23,21 @@ Timestamp time.Time `meddler:"timestamp,utctime"`
} // HashToSign builds the hash to be signed using BJJ pub key and the constant message -func (a *AccountCreationAuth) HashToSign() ([]byte, error) { +func (a *AccountCreationAuth) HashToSign(chainID uint16, + hermezContractAddr ethCommon.Address) ([]byte, error) { // Calculate message to be signed - const msg = "I authorize this babyjubjub key for hermez rollup account creation" - comp, err := a.BJJ.MarshalText() - if err != nil { - return nil, tracerr.Wrap(err) - } - // Hash message (msg || compressed-bjj) - return ethCrypto.Keccak256Hash([]byte(msg), comp).Bytes(), nil + var chainIDBytes [2]byte + binary.BigEndian.PutUint16(chainIDBytes[:], chainID) + // to hash: [AccountCreationAuthMsg | compressedBJJ | chainID | hermezContractAddr] + return ethCrypto.Keccak256Hash([]byte(AccountCreationAuthMsg), a.BJJ[:], chainIDBytes[:], + hermezContractAddr[:]).Bytes(), nil } // VerifySignature ensures that the Signature is done with the specified EthAddr -func (a *AccountCreationAuth) VerifySignature() bool { +func (a *AccountCreationAuth) VerifySignature(chainID uint16, + hermezContractAddr ethCommon.Address) bool { // Calculate hash to be signed - msg, err := a.HashToSign() + msg, err := a.HashToSign(chainID, hermezContractAddr) if err != nil { return false }
@@ -0,0 +1,41 @@
+package common + +import ( + "encoding/hex" + "testing" + + ethCommon "github.com/ethereum/go-ethereum/common" + ethCrypto "github.com/ethereum/go-ethereum/crypto" + "github.com/iden3/go-iden3-crypto/babyjub" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestAccountCreationAuth(t *testing.T) { + // Ethereum key + ethSk, err := ethCrypto.HexToECDSA("fad9c8855b740a0b7ed4c221dbad0f33a83a49cad6b3fe8d5817ac83d38b6a19") + require.NoError(t, err) + ethAddr := ethCrypto.PubkeyToAddress(ethSk.PublicKey) + + // BabyJubJub key + var sk babyjub.PrivateKey + _, err = hex.Decode(sk[:], []byte("0001020304050607080900010203040506070809000102030405060708090001")) + assert.NoError(t, err) + + chainID := uint16(0) + hermezContractAddr := ethCommon.HexToAddress("0xc344E203a046Da13b0B4467EB7B3629D0C99F6E6") + a := AccountCreationAuth{ + EthAddr: ethAddr, + BJJ: sk.Public().Compress(), + } + msg, err := a.HashToSign(chainID, hermezContractAddr) + assert.NoError(t, err) + assert.Equal(t, "cb5a7e44329ff430c81fec49fb2ac6741f02d5ec96cbcb618a6991f0a9c80ffd", hex.EncodeToString(msg)) + + // sign AccountCreationAuth with eth key + sig, err := ethCrypto.Sign(msg, ethSk) + assert.NoError(t, err) + a.Signature = sig + + assert.True(t, a.VerifySignature(chainID, hermezContractAddr)) +}
@@ -611,8 +611,10 @@
func TestAuth(t *testing.T) { test.WipeDB(l2DB.DB()) const nAuths = 5 + chainID := uint16(0) + hermezContractAddr := ethCommon.HexToAddress("0xc344E203a046Da13b0B4467EB7B3629D0C99F6E6") // Generate authorizations - auths := test.GenAuths(nAuths) + auths := test.GenAuths(nAuths, chainID, hermezContractAddr) for i := 0; i < len(auths); i++ { // Add to the DB err := l2DB.AddAccountCreationAuth(auths[i])
@@ -245,8 +245,9 @@ &api.Config{
RollupConstants: scConsts.Rollup, AuctionConstants: scConsts.Auction, WDelayerConstants: scConsts.WDelayer, + ChainID: chainIDU16, + HermezAddress: cfg.SmartContracts.Rollup, }, - chainIDU16, ) if err != nil { return nil, tracerr.Wrap(err)@@ -301,7 +302,6 @@ hdb *historydb.HistoryDB,
sdb *statedb.StateDB, l2db *l2db.L2DB, config *api.Config, - chainID uint16, ) (*NodeAPI, error) { engine := gin.Default() engine.NoRoute(handleNoRoute)@@ -313,7 +313,6 @@ hdb,
sdb, l2db, config, - chainID, ) if err != nil { return nil, tracerr.Wrap(err)
@@ -1,6 +1,7 @@
package test import ( + ethCommon "github.com/ethereum/go-ethereum/common" ethCrypto "github.com/ethereum/go-ethereum/crypto" "github.com/hermeznetwork/hermez-node/common" "github.com/iden3/go-iden3-crypto/babyjub"@@ -66,7 +67,7 @@ return nil
} // GenAuths generates account creation authorizations -func GenAuths(nAuths int) []*common.AccountCreationAuth { +func GenAuths(nAuths int, chainID uint16, hermezContractAddr ethCommon.Address) []*common.AccountCreationAuth { auths := []*common.AccountCreationAuth{} for i := 0; i < nAuths; i++ { // Generate keys@@ -81,7 +82,7 @@ EthAddr: ethCrypto.PubkeyToAddress(ethPrivK.PublicKey),
BJJ: bjjPrivK.Public().Compress(), } // Sign - h, err := auth.HashToSign() + h, err := auth.HashToSign(chainID, hermezContractAddr) if err != nil { panic(err) }
@@ -3,13 +3,17 @@
import ( "testing" + ethCommon "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/assert" ) func TestGenAuths(t *testing.T) { + chainID := uint16(0) + hermezContractAddr := ethCommon.HexToAddress("0xc344E203a046Da13b0B4467EB7B3629D0C99F6E6") + const nAuths = 5 - auths := GenAuths(nAuths) + auths := GenAuths(nAuths, chainID, hermezContractAddr) for _, auth := range auths { - assert.True(t, auth.VerifySignature()) + assert.True(t, auth.VerifySignature(chainID, hermezContractAddr)) } }