diff --git a/app/app_test.go b/app/app_test.go index cba83b368bf3..5dd99a012df0 100644 --- a/app/app_test.go +++ b/app/app_test.go @@ -30,7 +30,6 @@ func (tx testTx) Get(key interface{}) (value interface{}) { return nil } func (tx testTx) GetSignBytes() []byte { return nil } func (tx testTx) ValidateBasic() error { return nil } func (tx testTx) GetSigners() []crypto.Address { return nil } -func (tx testTx) GetTxBytes() []byte { return nil } func (tx testTx) GetFeePayer() crypto.Address { return nil } func (tx testTx) GetSignatures() []sdk.StdSignature { return nil } diff --git a/examples/basecoin/app/stores.go b/examples/basecoin/app/stores.go index d2c76c894928..c12ee5dca12d 100644 --- a/examples/basecoin/app/stores.go +++ b/examples/basecoin/app/stores.go @@ -38,8 +38,18 @@ func (app *BasecoinApp) initMultiStore() { // depends on initKeys() func (app *BasecoinApp) initAppStore() { - app.accStore = auth.NewAccountStore( - app.mainStoreKey, - types.NewAppAccountCodecFromWireCodec(app.cdc), + accStore := auth.NewAccountStore( + app.mainStoreKey, // where accounts are persisted. + &types.AppAccount{}, // prototype sdk.Account. ) + + // If there are additional interfaces & concrete types that need to be + // registered w/ wire.Codec, they can be registered here before the + // accStore is sealed. + // + // cdc := accStore.WireCodec() + // cdc.RegisterInterface(...) + // cdc.RegisterConcrete(...) + + app.accStore = accStore.Seal() } diff --git a/examples/basecoin/types/account.go b/examples/basecoin/types/account.go index f21d93ed58f9..28a1f41552ab 100644 --- a/examples/basecoin/types/account.go +++ b/examples/basecoin/types/account.go @@ -1,8 +1,6 @@ package types import ( - wire "github.com/tendermint/go-wire" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth" ) @@ -12,7 +10,11 @@ var _ sdk.Account = (*AppAccount)(nil) type AppAccount struct { auth.BaseAccount - // Custom extensions for this application. + // Custom extensions for this application. This is just an example of + // extending auth.BaseAccount with custom fields. + // + // This is compatible with the stock auth.AccountStore, since + // auth.AccountStore uses the flexible go-wire library. Name string } @@ -23,27 +25,3 @@ func (acc AppAccount) GetName() string { func (acc *AppAccount) SetName(name string) { acc.Name = name } - -//---------------------------------------- - -type AppAccountCodec struct { - cdc *wire.Codec -} - -func NewAppAccountCodecFromWireCodec(cdc *wire.Codec) AppAccountCodec { - return AppAccountCodec{cdc} -} - -func (_ AppAccountCodec) Prototype() interface{} { - return &AppAccount{} -} - -func (aac AppAccountCodec) Encode(o interface{}) (bz []byte, err error) { - return aac.cdc.MarshalBinary(o) -} - -func (aac AppAccountCodec) Decode(bz []byte) (o interface{}, err error) { - o = aac.Prototype() - err = aac.cdc.UnmarshalBinary(bz, o) - return o, err -} diff --git a/examples/dummy/main.go b/examples/dummy/main.go index d2cb25dd831d..59261a604a09 100644 --- a/examples/dummy/main.go +++ b/examples/dummy/main.go @@ -101,10 +101,6 @@ func (tx dummyTx) GetSigners() []crypto.Address { return nil } -func (tx dummyTx) GetTxBytes() []byte { - return tx.bytes -} - func (tx dummyTx) GetSignatures() []sdk.StdSignature { return nil } diff --git a/types/codec.go b/types/codec.go deleted file mode 100644 index 950aed260b1d..000000000000 --- a/types/codec.go +++ /dev/null @@ -1,14 +0,0 @@ -package types - -// A generic codec for a fixed type. -type Codec interface { - - // Returns a prototype (empty) object. - Prototype() interface{} - - // Encodes the object. - Encode(o interface{}) ([]byte, error) - - // Decodes an object. - Decode(bz []byte) (interface{}, error) -} diff --git a/types/tx_msg.go b/types/tx_msg.go index 57e0a97f45fd..1782e348b09d 100644 --- a/types/tx_msg.go +++ b/types/tx_msg.go @@ -2,7 +2,6 @@ package types import ( crypto "github.com/tendermint/go-crypto" - wire "github.com/tendermint/go-wire" ) type Msg interface { @@ -34,10 +33,6 @@ type Tx interface { // deducted before the Msg is processed. GetFeePayer() crypto.Address - // Get the canonical byte representation of the Tx. - // Includes any signatures (or empty slots). - GetTxBytes() []byte - // Signatures returns the signature of signers who signed the Msg. // CONTRACT: Length returned is same as length of // pubkeys returned from MsgKeySigners, and the order @@ -55,14 +50,7 @@ type StdTx struct { Signatures []StdSignature } -func (tx StdTx) GetFeePayer() crypto.Address { return tx.Signatures[0].PubKey.Address() } -func (tx StdTx) GetTxBytes() []byte { - bz, err := wire.MarshalBinary(tx) // XXX: this is bad - if err != nil { - panic(err) - } - return bz -} +func (tx StdTx) GetFeePayer() crypto.Address { return tx.Signatures[0].PubKey.Address() } func (tx StdTx) GetSignatures() []StdSignature { return tx.Signatures } type TxDecoder func(txBytes []byte) (Tx, error) diff --git a/x/auth/store.go b/x/auth/store.go index 1067d8d90919..188dd1afbb52 100644 --- a/x/auth/store.go +++ b/x/auth/store.go @@ -1,52 +1,134 @@ package auth import ( + "fmt" + "reflect" + crypto "github.com/tendermint/go-crypto" + wire "github.com/tendermint/go-wire" sdk "github.com/cosmos/cosmos-sdk/types" ) -// Implements sdk.AccountStore +// Implements sdk.AccountStore. +// This AccountStore encodes/decodes accounts using the +// go-wire (binary) encoding/decoding library. type accountStore struct { - key sdk.SubstoreKey - codec sdk.Codec + + // The (unexposed) key used to access the store from the Context. + key sdk.SubstoreKey + + // The prototypical sdk.Account concrete type. + proto sdk.Account + + // The wire codec for binary encoding/decoding of accounts. + cdc *wire.Codec } -func NewAccountStore(key sdk.SubstoreKey, codec sdk.Codec) accountStore { +// NewAccountStore returns a new sdk.AccountStore that +// uses go-wire to (binary) encode and decode concrete sdk.Accounts. +func NewAccountStore(key sdk.SubstoreKey, proto sdk.Account) accountStore { return accountStore{ key: key, - codec: codec, + proto: proto, + cdc: wire.NewCodec(), } } -// Implements sdk.AccountStore +// Returns the go-wire codec. You may need to register interfaces +// and concrete types here, if your app's sdk.Account +// implementation includes interface fields. +// NOTE: It is not secure to expose the codec, so check out +// .Seal(). +func (as accountStore) WireCodec() *wire.Codec { + return as.cdc +} + +// Returns a "sealed" accountStore. +// The codec is not accessible from a sealedAccountStore +func (as accountStore) Seal() sealedAccountStore { + return sealedAccountStore{as} +} + +// Implements sdk.AccountStore. func (as accountStore) NewAccountWithAddress(ctx sdk.Context, addr crypto.Address) sdk.Account { - acc := as.codec.Prototype().(sdk.Account) + acc := as.clonePrototype() acc.SetAddress(addr) return acc } -// Implements sdk.AccountStore +// Implements sdk.AccountStore. func (as accountStore) GetAccount(ctx sdk.Context, addr crypto.Address) sdk.Account { store := ctx.KVStore(as.key) bz := store.Get(addr) if bz == nil { - return nil // XXX + return nil } - o, err := as.codec.Decode(bz) - if err != nil { - panic(err) - } - return o.(sdk.Account) + acc := as.decodeAccount(bz) + return acc } -// Implements sdk.AccountStore +// Implements sdk.AccountStore. func (as accountStore) SetAccount(ctx sdk.Context, acc sdk.Account) { addr := acc.GetAddress() store := ctx.KVStore(as.key) - bz, err := as.codec.Encode(acc) + bz := as.encodeAccount(acc) + store.Set(addr, bz) +} + +//---------------------------------------- +// misc. + +// Creates a new struct (or pointer to struct) from as.proto. +func (as accountStore) clonePrototype() sdk.Account { + protoRt := reflect.TypeOf(as.proto) + if protoRt.Kind() == reflect.Ptr { + protoCrt := protoRt.Elem() + if protoCrt.Kind() != reflect.Struct { + panic("AccountStore requires a struct proto sdk.Account, or a pointer to one") + } + protoRv := reflect.New(protoCrt) + clone, ok := protoRv.Interface().(sdk.Account) + if !ok { + panic(fmt.Sprintf("AccountStore requires a proto sdk.Account, but %v doesn't implement sdk.Account", protoRt)) + } + return clone + } else { + protoRv := reflect.New(protoRt).Elem() + clone, ok := protoRv.Interface().(sdk.Account) + if !ok { + panic(fmt.Sprintf("AccountStore requires a proto sdk.Account, but %v doesn't implement sdk.Account", protoRt)) + } + return clone + } +} + +func (as accountStore) encodeAccount(acc sdk.Account) []byte { + bz, err := as.cdc.MarshalBinary(acc) if err != nil { panic(err) } - store.Set(addr, bz) + return bz +} + +func (as accountStore) decodeAccount(bz []byte) sdk.Account { + acc := as.clonePrototype() + err := as.cdc.UnmarshalBinary(bz, &acc) + if err != nil { + panic(err) + } + return acc +} + +//---------------------------------------- +// sealedAccountStore + +type sealedAccountStore struct { + accountStore +} + +// There's no way for external modules to mutate the +// sas.accountStore.ctx from here, even with reflection. +func (sas sealedAccountStore) WireCodec() *wire.Codec { + panic("accountStore is sealed") }