diff --git a/PENDING.md b/PENDING.md index 05dd59ee0..dd4fe4bb6 100644 --- a/PENDING.md +++ b/PENDING.md @@ -43,7 +43,8 @@ IMPROVEMENTS * Gaia * SDK - * \#1277 Complete bank module specification + - \#1277 Complete bank module specification + - \#2963 Complete auth module specification * \#2914 No longer withdraw validator rewards on bond/unbond, but rather move the rewards to the respective validator's pools. diff --git a/docs/spec/auth/README.md b/docs/spec/auth/README.md new file mode 100644 index 000000000..6db6db226 --- /dev/null +++ b/docs/spec/auth/README.md @@ -0,0 +1,29 @@ +# Auth module specification + +## Abstract + +This document specifies the auth module of the Cosmos SDK. + +The auth module is responsible for specifying the base transaction and account types +for an application, since the SDK itself is agnostic to these particulars. It contains +the ante handler, where all basic transaction validity checks (signatures, nonces, auxiliary fields) +are performed, and exposes the account keeper, which allows other modules to read, write, and modify accounts. + +This module will be used in the Cosmos Hub. + +## Contents + +1. **[State](state.md)** + 1. [Accounts](state.md#accounts) + 1. [Account Interface](state.md#account-interface) + 1. [Base Account](state.md#baseaccount) + 1. [Vesting Account](state.md#vestingaccount) +1. **[Types](types.md)** + 1. [StdFee](types.md#stdfee) + 1. [StdSignature](types.md#stdsignature) + 1. [StdTx](types.md#stdtx) + 1. [StdSignDoc](types.md#stdsigndoc) +1. **[Keepers](keepers.md)** + 1. [AccountKeeper](keepers.md#account-keeper) +1. **[Handlers](handlers.md)** + 1. [Ante Handler](handlers.md#ante-handler) diff --git a/docs/spec/auth/handlers.md b/docs/spec/auth/handlers.md new file mode 100644 index 000000000..2f8909c63 --- /dev/null +++ b/docs/spec/auth/handlers.md @@ -0,0 +1,38 @@ +## Handlers + +The auth module presently has no transaction handlers of its own, but does expose +the special `AnteHandler`, used for performing basic validity checks on a transaction, +such that it could be thrown out of the mempool. Note that the ante handler is called on +`CheckTx`, but *also* on `DeliverTx`, as Tendermint proposers presently have the ability +to include in their proposed block transactions which fail `CheckTx`. + +### Ante Handler + +```golang +anteHandler(ak AccountKeeper, fck FeeCollectionKeeper, tx sdk.Tx) + if !tx.(StdTx) + fail with "not a StdTx" + + if isCheckTx and tx.Fee < config.SubjectiveMinimumFee + fail with "insufficient fee for mempool inclusion" + + if tx.ValidateBasic() != nil + fail with "tx failed ValidateBasic" + + if tx.Fee > 0 + account = GetAccount(tx.GetSigners()[0]) + coins := acount.GetCoins() + if coins < tx.Fee + fail with "insufficient fee to pay for transaction" + account.SetCoins(coins - tx.Fee) + fck.AddCollectedFees(tx.Fee) + + for index, signature in tx.GetSignatures() + account = GetAccount(tx.GetSigners()[index]) + bytesToSign := StdSignBytes(chainID, acc.GetAccountNumber(), + acc.GetSequence(), tx.Fee, tx.Msgs, tx.Memo) + if !signature.Verify(bytesToSign) + fail with "invalid signature" + + return +``` diff --git a/docs/spec/auth/keepers.md b/docs/spec/auth/keepers.md new file mode 100644 index 000000000..27611decd --- /dev/null +++ b/docs/spec/auth/keepers.md @@ -0,0 +1,39 @@ +## Keepers + +The auth module only exposes one keeper, the account keeper, which can be used to read and write accounts. + +### Account Keeper + +Presently only one fully-permissioned account keeper is exposed, which has the ability to both read and write +all fields of all accounts, and to iterate over all stored accounts. + +```golang +type AccountKeeper interface { + // Return a new account with the next account number and the specified address. Does not save the new account to the store. + NewAccountWithAddress(AccAddress) Account + + // Return a new account with the next account number. Does not save the new account to the store. + NewAccount(Account) Account + + // Retrieve an account from the store + GetAccount(AccAddress) Account + + // Set an account in the store + SetAccount(Account) + + // Remove an account from the store + RemoveAccount(Account) + + // Iterate over all accounts, calling the provided function. Stop iteraiton when it returns false. + IterateAccounts(func(Account) (bool)) + + // Fetch the public key of an account at a specified address + GetPubKey(AccAddress) PubKey + + // Fetch the sequence of an account at a specified address + GetSequence(AccAddress) uint64 + + // Fetch the next account number, and increment the internal counter + GetNextAccountNumber() uint64 +} +``` diff --git a/docs/spec/auth/state.md b/docs/spec/auth/state.md index e69de29bb..29b5a93ab 100644 --- a/docs/spec/auth/state.md +++ b/docs/spec/auth/state.md @@ -0,0 +1,58 @@ +## State + +### Accounts + +Accounts contain authentication information for a uniquely identified external user of an SDK blockchain, +including public key, address, and account number / sequence number for replay protection. For efficiency, +since account balances must also be fetched to pay fees, account structs also store the balance of a user +as `sdk.Coins`. + +Accounts are exposed externally as an interface, and stored internally as +either a base account or vesting account. Module clients wishing to add more +account types may do so. + +- `0x01 | Address -> amino(account)` + +#### Account Interface + +The account interface exposes methods to read and write standard account information. +Note that all of these methods operate on an account struct confirming to the interface +- in order to write the account to the store, the account keeper will need to be used. + +```golang +type Account interface { + GetAddress() AccAddress + SetAddress(AccAddress) + + GetPubKey() PubKey + SetPubKey(PubKey) + + GetAccountNumber() uint64 + SetAccountNumber(uint64) + + GetSequence() uint64 + SetSequence(uint64) + + GetCoins() Coins + SetCoins(Coins) +} +``` + +#### Base Account + +A base account is the simplest and most common account type, which just stores all requisite +fields directly in a struct. + +```golang +type BaseAccount struct { + Address AccAddress + Coins Coins + PubKey PubKey + AccountNumber uint64 + Sequence uint64 +} +``` + +#### Vesting Account + +See [Vesting](vesting.md). diff --git a/docs/spec/auth/transactions.md b/docs/spec/auth/transactions.md deleted file mode 100644 index e69de29bb..000000000 diff --git a/docs/spec/auth/types.md b/docs/spec/auth/types.md new file mode 100644 index 000000000..30c5ac8b1 --- /dev/null +++ b/docs/spec/auth/types.md @@ -0,0 +1,65 @@ +## Types + +Besides accounts (specified in [State](state.md)), the types exposed by the auth module +are `StdFee`, the combination of an amount and gas limit, `StdSignature`, the combination +of an optional public key and a cryptographic signature as a byte array, `StdTx`, +a struct which implements the `sdk.Tx` interface using `StdFee` and `StdSignature`, and +`StdSignDoc`, a replay-prevention structure for `StdTx` which transaction senders must sign over. + +### StdFee + +A `StdFee` is simply the combination of a fee amount, in any number of denominations, +and a gas limit (where dividing the amount by the gas limit gives a "gas price"). + +```golang +type StdFee struct { + Amount Coins + Gas uint64 +} +``` + +### StdSignature + +A `StdSignature` is the combination of an optional public key and a cryptographic signature +as a byte array. The SDK is agnostic to particular key or signature formats and supports any +supported by the `PubKey` interface. + +```golang +type StdSignature struct { + PubKey PubKey + Signature []byte +} +``` + +### StdTx + +A `StdTx` is a struct which implements the `sdk.Tx` interface, and is likely to be generic +enough to serve the purposes of many Cosmos SDK blockchains. + +```golang +type StdTx struct { + Msgs []sdk.Msg + Fee StdFee + Signatures []StdSignature + Memo string +} +``` + +### StdSignDoc + +A `StdSignDoc` is a replay-prevention structure to be signed over, which ensures that +any submitted transaction (which is simply a signature over a particular bytestring) +will only be executable once on a particular blockchain. + +`json.RawMessage` is preferred over using the SDK types for future compatibility. + +```golang +type StdSignDoc struct { + AccountNumber uint64 + ChainID string + Fee json.RawMessage + Memo string + Msgs []json.RawMessage + Sequence uint64 +} +``` diff --git a/x/auth/keeper.go b/x/auth/keeper.go index deecaf2b2..18e82c206 100644 --- a/x/auth/keeper.go +++ b/x/auth/keeper.go @@ -6,7 +6,12 @@ import ( "github.com/tendermint/tendermint/crypto" ) -var globalAccountNumberKey = []byte("globalAccountNumber") +var ( + // Prefix for account-by-address store + AddressStoreKeyPrefix = []byte{0x01} + + globalAccountNumberKey = []byte("globalAccountNumber") +) // This AccountKeeper encodes/decodes accounts using the // go-amino (binary) encoding/decoding library. @@ -61,7 +66,7 @@ func (am AccountKeeper) NewAccount(ctx sdk.Context, acc Account) Account { // Turn an address to key used to get it from the account store func AddressStoreKey(addr sdk.AccAddress) []byte { - return append([]byte("account:"), addr.Bytes()...) + return append(AddressStoreKeyPrefix, addr.Bytes()...) } // Implements sdk.AccountKeeper. @@ -93,7 +98,7 @@ func (am AccountKeeper) RemoveAccount(ctx sdk.Context, acc Account) { // Implements sdk.AccountKeeper. func (am AccountKeeper) IterateAccounts(ctx sdk.Context, process func(Account) (stop bool)) { store := ctx.KVStore(am.key) - iter := sdk.KVStorePrefixIterator(store, []byte("account:")) + iter := sdk.KVStorePrefixIterator(store, AddressStoreKeyPrefix) defer iter.Close() for { if !iter.Valid() {