From ea6d3e8efc2c7172775659ebfcb870626e6ab841 Mon Sep 17 00:00:00 2001 From: Sunny Aggarwal Date: Mon, 7 May 2018 01:13:32 -0400 Subject: [PATCH] should work maybe? --- CHANGELOG.md | 1 + examples/basecoin/app/app.go | 1 + examples/basecoin/app/app_test.go | 55 +++++++++++++++++++++++++++ x/auth/ante.go | 7 ++-- x/auth/baseaccount.go | 3 -- x/auth/baseaccount_test.go | 6 +-- x/auth/handler.go | 33 ++++++++++++++++ x/auth/mapper.go | 63 +++++++++++++++++++++++++------ x/auth/msgs.go | 44 +++++++++++++++++++++ x/auth/msgs_test.go | 47 +++++++++++++++++++++++ 10 files changed, 238 insertions(+), 22 deletions(-) create mode 100644 x/auth/handler.go create mode 100644 x/auth/msgs.go create mode 100644 x/auth/msgs_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 01180ade9..b5c302d52 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -42,6 +42,7 @@ BREAKING CHANGES * gaiad init now requires use of `--name` flag * Removed Get from Msg interface * types/rational now extends big.Rat +* added ability to change pubkey to auth module FEATURES: diff --git a/examples/basecoin/app/app.go b/examples/basecoin/app/app.go index d3eae85c0..b1a434fa2 100644 --- a/examples/basecoin/app/app.go +++ b/examples/basecoin/app/app.go @@ -70,6 +70,7 @@ func NewBasecoinApp(logger log.Logger, db dbm.DB) *BasecoinApp { // register message routes app.Router(). + AddRoute("auth", auth.NewHandler(app.accountMapper.(auth.AccountMapper))). AddRoute("bank", bank.NewHandler(app.coinKeeper)). AddRoute("ibc", ibc.NewHandler(app.ibcMapper, app.coinKeeper)). AddRoute("stake", stake.NewHandler(app.stakeKeeper)) diff --git a/examples/basecoin/app/app_test.go b/examples/basecoin/app/app_test.go index b502aae46..034e19860 100644 --- a/examples/basecoin/app/app_test.go +++ b/examples/basecoin/app/app_test.go @@ -208,6 +208,61 @@ func TestGenesis(t *testing.T) { assert.Equal(t, acc, res1) } +func TestMsgChangePubKey(t *testing.T) { + + bapp := newBasecoinApp() + + // Construct some genesis bytes to reflect basecoin/types/AppAccount + // Give 77 foocoin to the first key + coins, err := sdk.ParseCoins("77foocoin") + require.Nil(t, err) + baseAcc := auth.BaseAccount{ + Address: addr1, + Coins: coins, + } + + // Construct genesis state + err = setGenesisAccounts(bapp, baseAcc) + assert.Nil(t, err) + // A checkTx context (true) + ctxCheck := bapp.BaseApp.NewContext(true, abci.Header{}) + res1 := bapp.accountMapper.GetAccount(ctxCheck, addr1) + assert.Equal(t, baseAcc, res1.(*types.AppAccount).BaseAccount) + + // Run a CheckDeliver + SignCheckDeliver(t, bapp, sendMsg1, []int64{0}, true, priv1) + + // Check balances + CheckBalance(t, bapp, addr1, "67foocoin") + CheckBalance(t, bapp, addr2, "10foocoin") + + changePubKeyMsg := auth.MsgChangeKey{ + Address: addr1, + NewPubKey: priv2.PubKey(), + } + + ctxDeliver := bapp.BaseApp.NewContext(false, abci.Header{}) + acc := bapp.accountMapper.GetAccount(ctxDeliver, addr1) + + // send a MsgChangePubKey + SignCheckDeliver(t, bapp, changePubKeyMsg, []int64{1}, true, priv1) + acc = bapp.accountMapper.GetAccount(ctxDeliver, addr1) + + assert.True(t, priv2.PubKey().Equals(acc.GetPubKey())) + + // signing a SendMsg with the old privKey should be an auth error + tx := genTx(sendMsg1, []int64{2}, priv1) + res := bapp.Deliver(tx) + assert.Equal(t, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeUnauthorized), res.Code, res.Log) + + // resigning the tx with the new correct priv key should work + SignCheckDeliver(t, bapp, sendMsg1, []int64{2}, true, priv2) + + // Check balances + CheckBalance(t, bapp, addr1, "57foocoin") + CheckBalance(t, bapp, addr2, "20foocoin") +} + func TestMsgSendWithAccounts(t *testing.T) { bapp := newBasecoinApp() diff --git a/x/auth/ante.go b/x/auth/ante.go index c7af7e2d9..4be1c6674 100644 --- a/x/auth/ante.go +++ b/x/auth/ante.go @@ -11,7 +11,7 @@ import ( // NewAnteHandler returns an AnteHandler that checks // and increments sequence numbers, checks signatures, // and deducts fees from the first signer. -func NewAnteHandler(accountMapper sdk.AccountMapper, feeHandler sdk.FeeHandler) sdk.AnteHandler { +func NewAnteHandler(am sdk.AccountMapper, feeHandler sdk.FeeHandler) sdk.AnteHandler { return func( ctx sdk.Context, tx sdk.Tx, ) (_ sdk.Context, _ sdk.Result, abort bool) { @@ -24,7 +24,6 @@ func NewAnteHandler(accountMapper sdk.AccountMapper, feeHandler sdk.FeeHandler) true } - // TODO: can tx just implement message? msg := tx.GetMsg() // TODO: will this always be a stdtx? should that be used in the function signature? @@ -62,7 +61,7 @@ func NewAnteHandler(accountMapper sdk.AccountMapper, feeHandler sdk.FeeHandler) // check signature, return account with incremented nonce signerAcc, res := processSig( - ctx, accountMapper, + ctx, am, signerAddr, sig, signBytes, ) if !res.IsOK() { @@ -82,7 +81,7 @@ func NewAnteHandler(accountMapper sdk.AccountMapper, feeHandler sdk.FeeHandler) } // Save the account. - accountMapper.SetAccount(ctx, signerAcc) + am.SetAccount(ctx, signerAcc) signerAccs[i] = signerAcc } diff --git a/x/auth/baseaccount.go b/x/auth/baseaccount.go index 6a612689d..ff907fc38 100644 --- a/x/auth/baseaccount.go +++ b/x/auth/baseaccount.go @@ -51,9 +51,6 @@ func (acc BaseAccount) GetPubKey() crypto.PubKey { // Implements sdk.Account. func (acc *BaseAccount) SetPubKey(pubKey crypto.PubKey) error { - if acc.PubKey != nil { - return errors.New("cannot override BaseAccount pubkey") - } acc.PubKey = pubKey return nil } diff --git a/x/auth/baseaccount_test.go b/x/auth/baseaccount_test.go index 8b69b6dfc..d3363e4fb 100644 --- a/x/auth/baseaccount_test.go +++ b/x/auth/baseaccount_test.go @@ -37,10 +37,10 @@ func TestBaseAccountAddressPubKey(t *testing.T) { assert.Nil(t, err) assert.Equal(t, pub1, acc.GetPubKey()) - // can't override pubkey + // can override pubkey err = acc.SetPubKey(pub2) - assert.NotNil(t, err) - assert.Equal(t, pub1, acc.GetPubKey()) + assert.Nil(t, err) + assert.Equal(t, pub2, acc.GetPubKey()) //------------------------------------ diff --git a/x/auth/handler.go b/x/auth/handler.go new file mode 100644 index 000000000..bba03c56b --- /dev/null +++ b/x/auth/handler.go @@ -0,0 +1,33 @@ +package auth + +import ( + "reflect" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// NewHandler returns a handler for "auth" type messages. +func NewHandler(am AccountMapper) sdk.Handler { + return func(ctx sdk.Context, msg sdk.Msg) sdk.Result { + switch msg := msg.(type) { + case MsgChangeKey: + return handleMsgChangeKey(ctx, am, msg) + default: + errMsg := "Unrecognized auth Msg type: " + reflect.TypeOf(msg).Name() + return sdk.ErrUnknownRequest(errMsg).Result() + } + } +} + +// Handle MsgChangeKey +// Should be very expensive, because once this happens, an account is un-prunable +func handleMsgChangeKey(ctx sdk.Context, am AccountMapper, msg MsgChangeKey) sdk.Result { + + err := am.setPubKey(ctx, msg.Address, msg.NewPubKey) + if err != nil { + return err.Result() + } + + // TODO: add some tags so we can search it! + return sdk.Result{} // TODO +} diff --git a/x/auth/mapper.go b/x/auth/mapper.go index f9e202c8a..3666f13b6 100644 --- a/x/auth/mapper.go +++ b/x/auth/mapper.go @@ -6,14 +6,15 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" wire "github.com/cosmos/cosmos-sdk/wire" + crypto "github.com/tendermint/go-crypto" ) -var _ sdk.AccountMapper = (*accountMapper)(nil) +var _ sdk.AccountMapper = (*AccountMapper)(nil) // Implements sdk.AccountMapper. // This AccountMapper encodes/decodes accounts using the // go-amino (binary) encoding/decoding library. -type accountMapper struct { +type AccountMapper struct { // The (unexposed) key used to access the store from the Context. key sdk.StoreKey @@ -28,23 +29,23 @@ type accountMapper struct { // NewAccountMapper returns a new sdk.AccountMapper that // uses go-amino to (binary) encode and decode concrete sdk.Accounts. // nolint -func NewAccountMapper(cdc *wire.Codec, key sdk.StoreKey, proto sdk.Account) accountMapper { - return accountMapper{ +func NewAccountMapper(cdc *wire.Codec, key sdk.StoreKey, proto sdk.Account) AccountMapper { + return AccountMapper{ key: key, proto: proto, cdc: cdc, } } -// Implements sdk.AccountMapper. -func (am accountMapper) NewAccountWithAddress(ctx sdk.Context, addr sdk.Address) sdk.Account { +// Implaements sdk.AccountMapper. +func (am AccountMapper) NewAccountWithAddress(ctx sdk.Context, addr sdk.Address) sdk.Account { acc := am.clonePrototype() acc.SetAddress(addr) return acc } // Implements sdk.AccountMapper. -func (am accountMapper) GetAccount(ctx sdk.Context, addr sdk.Address) sdk.Account { +func (am AccountMapper) GetAccount(ctx sdk.Context, addr sdk.Address) sdk.Account { store := ctx.KVStore(am.key) bz := store.Get(addr) if bz == nil { @@ -55,7 +56,7 @@ func (am accountMapper) GetAccount(ctx sdk.Context, addr sdk.Address) sdk.Accoun } // Implements sdk.AccountMapper. -func (am accountMapper) SetAccount(ctx sdk.Context, acc sdk.Account) { +func (am AccountMapper) SetAccount(ctx sdk.Context, acc sdk.Account) { addr := acc.GetAddress() store := ctx.KVStore(am.key) bz := am.encodeAccount(acc) @@ -63,7 +64,7 @@ func (am accountMapper) SetAccount(ctx sdk.Context, acc sdk.Account) { } // Implements sdk.AccountMapper. -func (am accountMapper) IterateAccounts(ctx sdk.Context, process func(sdk.Account) (stop bool)) { +func (am AccountMapper) IterateAccounts(ctx sdk.Context, process func(sdk.Account) (stop bool)) { store := ctx.KVStore(am.key) iter := store.Iterator(nil, nil) for { @@ -79,11 +80,49 @@ func (am accountMapper) IterateAccounts(ctx sdk.Context, process func(sdk.Accoun } } +// Returns the PubKey of the account at address +func (am AccountMapper) GetPubKey(ctx sdk.Context, addr sdk.Address) (crypto.PubKey, sdk.Error) { + acc := am.GetAccount(ctx, addr) + if acc == nil { + return nil, sdk.ErrUnknownAddress(addr.String()) + } + return acc.GetPubKey(), nil +} + +func (am AccountMapper) setPubKey(ctx sdk.Context, addr sdk.Address, newPubKey crypto.PubKey) sdk.Error { + acc := am.GetAccount(ctx, addr) + if acc == nil { + return sdk.ErrUnknownAddress(addr.String()) + } + acc.SetPubKey(newPubKey) + am.SetAccount(ctx, acc) + return nil +} + +// Returns the Sequence of the account at address +func (am AccountMapper) GetSequence(ctx sdk.Context, addr sdk.Address) (int64, sdk.Error) { + acc := am.GetAccount(ctx, addr) + if acc == nil { + return 0, sdk.ErrUnknownAddress(addr.String()) + } + return acc.GetSequence(), nil +} + +func (am AccountMapper) setSequence(ctx sdk.Context, addr sdk.Address, newSequence int64) sdk.Error { + acc := am.GetAccount(ctx, addr) + if acc == nil { + return sdk.ErrUnknownAddress(addr.String()) + } + acc.SetSequence(newSequence) + am.SetAccount(ctx, acc) + return nil +} + //---------------------------------------- // misc. // Creates a new struct (or pointer to struct) from am.proto. -func (am accountMapper) clonePrototype() sdk.Account { +func (am AccountMapper) clonePrototype() sdk.Account { protoRt := reflect.TypeOf(am.proto) if protoRt.Kind() == reflect.Ptr { protoCrt := protoRt.Elem() @@ -106,7 +145,7 @@ func (am accountMapper) clonePrototype() sdk.Account { return clone } -func (am accountMapper) encodeAccount(acc sdk.Account) []byte { +func (am AccountMapper) encodeAccount(acc sdk.Account) []byte { bz, err := am.cdc.MarshalBinaryBare(acc) if err != nil { panic(err) @@ -114,7 +153,7 @@ func (am accountMapper) encodeAccount(acc sdk.Account) []byte { return bz } -func (am accountMapper) decodeAccount(bz []byte) (acc sdk.Account) { +func (am AccountMapper) decodeAccount(bz []byte) (acc sdk.Account) { err := am.cdc.UnmarshalBinaryBare(bz, &acc) if err != nil { panic(err) diff --git a/x/auth/msgs.go b/x/auth/msgs.go new file mode 100644 index 000000000..545b296e5 --- /dev/null +++ b/x/auth/msgs.go @@ -0,0 +1,44 @@ +package auth + +import ( + "encoding/json" + + "github.com/tendermint/go-crypto" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// MsgChangeKey - high level transaction of the auth module +type MsgChangeKey struct { + Address sdk.Address `json:"address"` + NewPubKey crypto.PubKey `json:"public_key"` +} + +var _ sdk.Msg = MsgChangeKey{} + +// NewMsgChangeKey - msg to claim an account and set the PubKey +func NewMsgChangeKey(addr sdk.Address, pubkey crypto.PubKey) MsgChangeKey { + return MsgChangeKey{Address: addr, NewPubKey: pubkey} +} + +// Implements Msg. +func (msg MsgChangeKey) Type() string { return "auth" } + +// Implements Msg. +func (msg MsgChangeKey) ValidateBasic() sdk.Error { + return nil +} + +// Implements Msg. +func (msg MsgChangeKey) GetSignBytes() []byte { + b, err := json.Marshal(msg) // XXX: ensure some canonical form + if err != nil { + panic(err) + } + return b +} + +// Implements Msg. +func (msg MsgChangeKey) GetSigners() []sdk.Address { + return []sdk.Address{msg.Address} +} diff --git a/x/auth/msgs_test.go b/x/auth/msgs_test.go new file mode 100644 index 000000000..30c98b073 --- /dev/null +++ b/x/auth/msgs_test.go @@ -0,0 +1,47 @@ +package auth + +import ( + "testing" + + "github.com/stretchr/testify/assert" + crypto "github.com/tendermint/go-crypto" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func TestNewMsgChangeKey(t *testing.T) {} + +func TestMsgChangeKeyType(t *testing.T) { + addr1 := sdk.Address([]byte("input")) + newPubKey := crypto.GenPrivKeyEd25519().PubKey() + + var msg = MsgChangeKey{ + Address: addr1, + NewPubKey: newPubKey, + } + + assert.Equal(t, msg.Type(), "auth") +} + +func TestMsgChangeKeyValidation(t *testing.T) { + + addr1 := sdk.Address([]byte("input")) + + // emptyPubKey := crypto.PubKeyEd25519{} + // var msg = MsgChangeKey{ + // Address: addr1, + // NewPubKey: emptyPubKey, + // } + + // // fmt.Println(msg.NewPubKey.Empty()) + // fmt.Println(msg.NewPubKey.Bytes()) + + // assert.NotNil(t, msg.ValidateBasic()) + + newPubKey := crypto.GenPrivKeyEd25519().PubKey() + msg := MsgChangeKey{ + Address: addr1, + NewPubKey: newPubKey, + } + assert.Nil(t, msg.ValidateBasic()) +}