From 8615a462abde6791af271937b627005c1d792ca8 Mon Sep 17 00:00:00 2001 From: arnaucube Date: Wed, 23 Dec 2020 18:11:53 +0100 Subject: [PATCH] Update AccountCreationAuth & fix auth.HashToSign - Fix AccountCreationAuth.HashToSign (was using `[]byte("0x...")`, which uses the bytes of the string. Now uses the bytearray of the compressed BJJ public key (compatible with js implementation)) - Update AccountCreationAuth to last specification (add to hash parameters ChainID & HermezAddress) - Add missing test to AccountCreationAuth --- api/accountcreationauths.go | 2 +- api/api.go | 24 +++++++++-------- api/api_test.go | 5 ++-- api/config.go | 3 +++ api/config_test.go | 5 +++- common/accountcreationauths.go | 29 +++++++++++--------- common/accountcreationauths_test.go | 41 +++++++++++++++++++++++++++++ db/l2db/l2db_test.go | 4 ++- node/node.go | 5 ++-- test/l2db.go | 5 ++-- test/l2db_test.go | 8 ++++-- 11 files changed, 95 insertions(+), 36 deletions(-) create mode 100644 common/accountcreationauths_test.go diff --git a/api/accountcreationauths.go b/api/accountcreationauths.go index 34566c9..e2c17ed 100644 --- a/api/accountcreationauths.go +++ b/api/accountcreationauths.go @@ -21,7 +21,7 @@ func (a *API) postAccountCreationAuth(c *gin.Context) { } // 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 } diff --git a/api/api.go b/api/api.go index 32fd404..5f73adb 100644 --- a/api/api.go +++ b/api/api.go @@ -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 @@ type Status struct { // 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 @@ func NewAPI( 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 @@ func NewAPI( 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 diff --git a/api/api_test.go b/api/api_test.go index 2651518..b71e364 100644 --- a/api/api_test.go +++ b/api/api_test.go @@ -221,7 +221,7 @@ func TestMain(m *testing.M) { 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 @@ func TestMain(m *testing.M) { sdb, l2DB, &_config, - chainID, ) if err != nil { panic(err) @@ -423,7 +422,7 @@ func TestMain(m *testing.M) { 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( diff --git a/api/config.go b/api/config.go index 9418c24..93292c9 100644 --- a/api/config.go +++ b/api/config.go @@ -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 { diff --git a/api/config_test.go b/api/config_test.go index 0487dbe..331f70c 100644 --- a/api/config_test.go +++ b/api/config_test.go @@ -9,7 +9,7 @@ import ( "github.com/stretchr/testify/assert" ) -func getConfigTest() Config { +func getConfigTest(chainID uint16) Config { var config Config var rollupPublicConstants common.RollupConstants @@ -40,6 +40,9 @@ func getConfigTest() Config { config.AuctionConstants = auctionConstants config.WDelayerConstants = wdelayerConstants + config.ChainID = chainID + config.HermezAddress = ethCommon.HexToAddress("0xc344E203a046Da13b0B4467EB7B3629D0C99F6E6") + return config } diff --git a/common/accountcreationauths.go b/common/accountcreationauths.go index 0e34688..de0bd1e 100644 --- a/common/accountcreationauths.go +++ b/common/accountcreationauths.go @@ -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 @@ type AccountCreationAuth struct { } // 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 } diff --git a/common/accountcreationauths_test.go b/common/accountcreationauths_test.go new file mode 100644 index 0000000..90f541c --- /dev/null +++ b/common/accountcreationauths_test.go @@ -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)) +} diff --git a/db/l2db/l2db_test.go b/db/l2db/l2db_test.go index 73b2b3d..155d08f 100644 --- a/db/l2db/l2db_test.go +++ b/db/l2db/l2db_test.go @@ -611,8 +611,10 @@ func TestPurge(t *testing.T) { 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]) diff --git a/node/node.go b/node/node.go index 9e38083..2f1a2d4 100644 --- a/node/node.go +++ b/node/node.go @@ -245,8 +245,9 @@ func NewNode(mode Mode, cfg *config.Node) (*Node, error) { 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 @@ func NewNodeAPI( sdb *statedb.StateDB, l2db *l2db.L2DB, config *api.Config, - chainID uint16, ) (*NodeAPI, error) { engine := gin.Default() engine.NoRoute(handleNoRoute) @@ -313,7 +313,6 @@ func NewNodeAPI( sdb, l2db, config, - chainID, ) if err != nil { return nil, tracerr.Wrap(err) diff --git a/test/l2db.go b/test/l2db.go index 39531c5..d9acf09 100644 --- a/test/l2db.go +++ b/test/l2db.go @@ -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 @@ func GenPoolTxs(n int, tokens []common.Token) []*common.PoolL2Tx { } // 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 @@ func GenAuths(nAuths int) []*common.AccountCreationAuth { BJJ: bjjPrivK.Public().Compress(), } // Sign - h, err := auth.HashToSign() + h, err := auth.HashToSign(chainID, hermezContractAddr) if err != nil { panic(err) } diff --git a/test/l2db_test.go b/test/l2db_test.go index 38b1b2f..8d2308a 100644 --- a/test/l2db_test.go +++ b/test/l2db_test.go @@ -3,13 +3,17 @@ package test 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)) } }