Merge pull request #943 from cosmos/sunny/change_pubkey

Change PubKey
This commit is contained in:
Christopher Goes 2018-05-16 00:29:29 +02:00 committed by GitHub
commit 2654959414
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 239 additions and 22 deletions

View File

@ -12,6 +12,7 @@ FEATURES
* [gaiad] Added `gaiad export` command to export current state to JSON
* [x/bank] Tx tags with sender/recipient for indexing & later retrieval
* [x/stake] Tx tags with delegator/candidate for delegation & unbonding, and candidate info for declare candidate / edit candidacy
* [x/auth] Added ability to change pubkey to auth module
IMPROVEMENTS

View File

@ -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))

View File

@ -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()

View File

@ -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
}

View File

@ -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
}

View File

@ -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())
//------------------------------------

34
x/auth/handler.go Normal file
View File

@ -0,0 +1,34 @@
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()
}
return sdk.Result{
Tags: sdk.NewTags("action", []byte("changePubkey"), "address", msg.Address.Bytes(), "pubkey", msg.NewPubKey.Bytes()),
}
}

View File

@ -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)

44
x/auth/msgs.go Normal file
View File

@ -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}
}

47
x/auth/msgs_test.go Normal file
View File

@ -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())
}