diff --git a/Makefile b/Makefile index d0694bd5f..a0bfd5f3d 100644 --- a/Makefile +++ b/Makefile @@ -28,7 +28,7 @@ test: build draw_deps: # requires brew install graphviz go get github.com/hirokidaichi/goviz - goviz -i github.com/tendermint/tendermint/cmd/tendermint | dot -Tpng -o hoge.png + goviz -i github.com/tendermint/tendermint/cmd/tendermint | dot -Tpng -o huge.png list_deps: go list -f '{{join .Deps "\n"}}' github.com/tendermint/tendermint/... | xargs go list -f '{{if not .Standard}}{{.ImportPath}}{{end}}' diff --git a/account/account.go b/account/account.go index ecad6c8c2..3178a9241 100644 --- a/account/account.go +++ b/account/account.go @@ -45,7 +45,7 @@ type Account struct { Code []byte `json:"code"` // VM code StorageRoot []byte `json:"storage_root"` // VM storage merkle root. - Permissions *ptypes.AccountPermissions `json:"permissions"` + Permissions ptypes.AccountPermissions `json:"permissions"` } func (acc *Account) Copy() *Account { @@ -55,7 +55,7 @@ func (acc *Account) Copy() *Account { func (acc *Account) String() string { // return fmt.Sprintf("Account{%X:%v C:%v S:%X}", acc.Address, acc.PubKey, len(acc.Code), acc.StorageRoot) - return fmt.Sprintf("Account{%X:%v C:%v S:%X P:(%s)}", acc.Address, acc.PubKey, len(acc.Code), acc.StorageRoot, acc.Permissions) + return fmt.Sprintf("Account{%X:%v C:%v S:%X P:%s}", acc.Address, acc.PubKey, len(acc.Code), acc.StorageRoot, acc.Permissions) } func AccountEncoder(o interface{}, w io.Writer, n *int64, err *error) { diff --git a/permission/types/errors.go b/permission/types/errors.go index 5a170a7cc..4b1d7cb94 100644 --- a/permission/types/errors.go +++ b/permission/types/errors.go @@ -14,27 +14,6 @@ func (e ErrInvalidPermission) Error() string { return fmt.Sprintf("invalid permission %d", e) } -// unknown string for permission -type ErrInvalidPermissionString string - -func (e ErrInvalidPermissionString) Error() string { - return fmt.Sprintf("invalid permission '%s'", e) -} - -// already exists (err on add) -type ErrPermissionExists string - -func (e ErrPermissionExists) Error() string { - return fmt.Sprintf("permission '%s' already exists", e) -} - -// unknown string for snative contract -type ErrInvalidSNativeString string - -func (e ErrInvalidSNativeString) Error() string { - return fmt.Sprintf("invalid snative contract '%s'", e) -} - // set=false. This error should be caught and the global // value fetched for the permission by the caller type ErrValueNotSet PermFlag diff --git a/permission/types/permissions.go b/permission/types/permissions.go index 7c9c626b8..2516efc4e 100644 --- a/permission/types/permissions.go +++ b/permission/types/permissions.go @@ -3,7 +3,6 @@ package types import ( "fmt" . "github.com/tendermint/tendermint/common" - "reflect" ) //------------------------------------------------------------------------------------------------ @@ -20,34 +19,34 @@ type PermFlag uint64 // Base permission references are like unix (the index is already bit shifted) const ( - Root PermFlag = 1 << iota // 1 - Send // 2 - Call // 4 - CreateContract // 8 - CreateAccount // 16 - Bond // 32 - Name // 64 + Root PermFlag = 1 << iota // 1 + Send // 2 + Call // 4 + CreateContract // 8 + CreateAccount // 16 + Bond // 32 + Name // 64 + NumBasePermissions uint = 7 // NOTE Adjust this too. - DefaultBBPB = Send | Call | CreateContract | CreateAccount | Bond | Name - - // XXX: must be adjusted if base perms added/removed - NumBasePermissions uint = 7 - TopBasePermission PermFlag = 1 << (NumBasePermissions - 1) - AllBasePermissions PermFlag = TopBasePermission | (TopBasePermission - 1) - - AllSet PermFlag = AllBasePermissions | AllSNativePermissions + TopBasePermFlag PermFlag = 1 << (NumBasePermissions - 1) + AllBasePermFlags PermFlag = TopBasePermFlag | (TopBasePermFlag - 1) + AllPermFlags PermFlag = AllBasePermFlags | AllSNativePermFlags + DefaultBasePermFlags PermFlag = Send | Call | CreateContract | CreateAccount | Bond | Name ) -// should have same ordering as above -type BasePermissionsString struct { - Root bool `json:"root,omitempty"` - Send bool `json:"send,omitempty"` - Call bool `json:"call,omitempty"` - CreateContract bool `json:"create_contract,omitempty"` - CreateAccount bool `json:"create_account,omitempty"` - Bond bool `json:"bond,omitempty"` - Name bool `json:"name,omitempty"` -} +var ( + ZeroBasePermissions = BasePermissions{0, 0} + ZeroAccountPermissions = AccountPermissions{ + Base: ZeroBasePermissions, + } + DefaultAccountPermissions = AccountPermissions{ + Base: BasePermissions{ + Perms: DefaultBasePermFlags, + SetBit: AllPermFlags, + }, + Roles: []string{}, + } +) //--------------------------------------------------------------------------------------------- @@ -60,10 +59,6 @@ type BasePermissions struct { SetBit PermFlag `json:"set"` } -func NewBasePermissions() *BasePermissions { - return &BasePermissions{0, 0} -} - // Get a permission value. ty should be a power of 2. // ErrValueNotSet is returned if the permission's set bit is off, // and should be caught by caller so the global permission can be fetched @@ -108,32 +103,15 @@ func (p *BasePermissions) IsSet(ty PermFlag) bool { return p.SetBit&ty > 0 } -func (p *BasePermissions) Copy() *BasePermissions { - if p == nil { - return nil - } - return &BasePermissions{ - Perms: p.Perms, - SetBit: p.SetBit, - } -} - -func (p *BasePermissions) String() string { +func (p BasePermissions) String() string { return fmt.Sprintf("Base: %b; Set: %b", p.Perms, p.SetBit) } //--------------------------------------------------------------------------------------------- type AccountPermissions struct { - Base *BasePermissions `json:"base"` - Roles []string `json:"roles"` -} - -func NewAccountPermissions() *AccountPermissions { - return &AccountPermissions{ - Base: NewBasePermissions(), - Roles: []string{}, - } + Base BasePermissions `json:"base"` + Roles []string `json:"roles"` } // Returns true if the role is found @@ -175,75 +153,7 @@ func (aP *AccountPermissions) RmRole(role string) bool { return false } -func (aP *AccountPermissions) Copy() *AccountPermissions { - if aP == nil { - return nil - } - r := make([]string, len(aP.Roles)) - copy(r, aP.Roles) - return &AccountPermissions{ - Base: aP.Base.Copy(), - Roles: r, - } -} - -func NewDefaultAccountPermissions() *AccountPermissions { - return &AccountPermissions{ - Base: &BasePermissions{ - Perms: DefaultBBPB, - SetBit: AllSet, - }, - Roles: []string{}, - } -} - -//--------------------------------------------------------------------------------------------- -// Utilities to make bitmasks human readable - -func NewDefaultAccountPermissionsString() BasePermissionsString { - return BasePermissionsString{ - Root: false, - Bond: true, - Send: true, - Call: true, - Name: true, - CreateAccount: true, - CreateContract: true, - } -} - -func AccountPermissionsFromStrings(perms *BasePermissionsString, roles []string) (*AccountPermissions, error) { - base := NewBasePermissions() - permRv := reflect.ValueOf(perms) - for i := uint(0); i < uint(permRv.NumField()); i++ { - v := permRv.Field(int(i)).Bool() - base.Set(1< ptypes.AllBasePermissions { + if perm > ptypes.AllBasePermFlags { panic("Checking an unknown permission in state should never happen") } @@ -826,10 +824,14 @@ func HasPermission(state AccountGetter, acc *account.Account, perm ptypes.PermFl v, err := acc.Permissions.Base.Get(perm) if _, ok := err.(ptypes.ErrValueNotSet); ok { + log.Debug("Account does not have permission", "account", acc, "accPermissions", acc.Permissions, "perm", perm) if state == nil { panic("All known global permissions should be set!") } + log.Debug("Querying GlobalPermissionsAddress") return HasPermission(nil, state.GetAccount(ptypes.GlobalPermissionsAddress), perm) + } else { + log.Debug("Account has permission", "account", acc, "accPermissions", acc.Permissions, "perm", perm) } return v } diff --git a/state/genesis.go b/state/genesis.go index 34d0432a0..dfd333be4 100644 --- a/state/genesis.go +++ b/state/genesis.go @@ -88,9 +88,9 @@ func MakeGenesisState(db dbm.DB, genDoc *GenesisDoc) *State { // Make accounts state tree accounts := merkle.NewIAVLTree(binary.BasicCodec, account.AccountCodec, defaultAccountsCacheCapacity, db) for _, genAcc := range genDoc.Accounts { - perm := ptypes.NewDefaultAccountPermissions() + perm := ptypes.ZeroAccountPermissions if genAcc.Permissions != nil { - perm = genAcc.Permissions + perm = *genAcc.Permissions } acc := &account.Account{ Address: genAcc.Address, @@ -104,12 +104,12 @@ func MakeGenesisState(db dbm.DB, genDoc *GenesisDoc) *State { // global permissions are saved as the 0 address // so they are included in the accounts tree - globalPerms := ptypes.NewDefaultAccountPermissions() + globalPerms := ptypes.DefaultAccountPermissions if genDoc.Params != nil && genDoc.Params.GlobalPermissions != nil { - globalPerms = genDoc.Params.GlobalPermissions + globalPerms = *genDoc.Params.GlobalPermissions // XXX: make sure the set bits are all true // Without it the HasPermission() functions will fail - globalPerms.Base.SetBit = ptypes.AllSet + globalPerms.Base.SetBit = ptypes.AllPermFlags } permsAcc := &account.Account{ diff --git a/state/permissions_test.go b/state/permissions_test.go index 5ffd02441..606491485 100644 --- a/state/permissions_test.go +++ b/state/permissions_test.go @@ -92,16 +92,17 @@ func makeUsers(n int) []*account.PrivAccount { } var ( - PermsAllFalse = ptypes.NewAccountPermissions() + PermsAllFalse = ptypes.ZeroAccountPermissions ) -func newBaseGenDoc(globalPerm, accountPerm *ptypes.AccountPermissions) GenesisDoc { +func newBaseGenDoc(globalPerm, accountPerm ptypes.AccountPermissions) GenesisDoc { genAccounts := []GenesisAccount{} for _, u := range user[:5] { + accountPermCopy := accountPerm // Create new instance for custom overridability. genAccounts = append(genAccounts, GenesisAccount{ Address: u.Address, Amount: 1000000, - Permissions: accountPerm.Copy(), + Permissions: &accountPermCopy, }) } @@ -109,7 +110,7 @@ func newBaseGenDoc(globalPerm, accountPerm *ptypes.AccountPermissions) GenesisDo GenesisTime: time.Now(), ChainID: chainID, Params: &GenesisParams{ - GlobalPermissions: globalPerm, + GlobalPermissions: &globalPerm, }, Accounts: genAccounts, Validators: []GenesisValidator{ @@ -353,7 +354,7 @@ func TestCallPermission(t *testing.T) { Code: []byte{0x60}, Sequence: 0, StorageRoot: Zero256.Bytes(), - Permissions: ptypes.NewAccountPermissions(), + Permissions: ptypes.ZeroAccountPermissions, } st.UpdateAccount(simpleAcc) @@ -377,7 +378,7 @@ func TestCallPermission(t *testing.T) { Code: contractCode, Sequence: 0, StorageRoot: Zero256.Bytes(), - Permissions: ptypes.NewAccountPermissions(), + Permissions: ptypes.ZeroAccountPermissions, } blockCache.UpdateAccount(caller1Acc) @@ -421,7 +422,7 @@ func TestCallPermission(t *testing.T) { Code: contractCode2, Sequence: 0, StorageRoot: Zero256.Bytes(), - Permissions: ptypes.NewAccountPermissions(), + Permissions: ptypes.ZeroAccountPermissions, } caller1Acc.Permissions.Base.Set(ptypes.Call, false) caller2Acc.Permissions.Base.Set(ptypes.Call, true) @@ -553,7 +554,7 @@ func TestCreatePermission(t *testing.T) { Code: code, Sequence: 0, StorageRoot: Zero256.Bytes(), - Permissions: ptypes.NewAccountPermissions(), + Permissions: ptypes.ZeroAccountPermissions, } contractAcc.Permissions.Base.Set(ptypes.Call, true) contractAcc.Permissions.Base.Set(ptypes.CreateContract, true) @@ -805,7 +806,7 @@ func TestCreateAccountPermission(t *testing.T) { Code: contractCode, Sequence: 0, StorageRoot: Zero256.Bytes(), - Permissions: ptypes.NewAccountPermissions(), + Permissions: ptypes.ZeroAccountPermissions, } blockCache.UpdateAccount(caller1Acc) @@ -856,7 +857,7 @@ func TestSNativeCALL(t *testing.T) { Code: nil, Sequence: 0, StorageRoot: Zero256.Bytes(), - Permissions: ptypes.NewAccountPermissions(), + Permissions: ptypes.ZeroAccountPermissions, } doug.Permissions.Base.Set(ptypes.Call, true) blockCache.UpdateAccount(doug) diff --git a/state/test.go b/state/test.go index a676498f7..b4e7c1847 100644 --- a/state/test.go +++ b/state/test.go @@ -25,7 +25,7 @@ func Tempfile(prefix string) (*os.File, string) { func RandAccount(randBalance bool, minBalance int64) (*account.Account, *account.PrivAccount) { privAccount := account.GenPrivAccount() - perms := ptypes.NewDefaultAccountPermissions() + perms := ptypes.DefaultAccountPermissions acc := &account.Account{ Address: privAccount.PubKey.Address(), PubKey: privAccount.PubKey, @@ -73,12 +73,13 @@ func RandGenesisState(numAccounts int, randBalance bool, minBalance int64, numVa db := dbm.NewMemDB() accounts := make([]GenesisAccount, numAccounts) privAccounts := make([]*account.PrivAccount, numAccounts) + defaultPerms := ptypes.DefaultAccountPermissions for i := 0; i < numAccounts; i++ { account, privAccount := RandAccount(randBalance, minBalance) accounts[i] = GenesisAccount{ Address: account.Address, Amount: account.Balance, - Permissions: ptypes.NewDefaultAccountPermissions(), + Permissions: &defaultPerms, // This will get copied into each state.Account. } privAccounts[i] = privAccount } @@ -105,7 +106,7 @@ func RandGenesisState(numAccounts int, randBalance bool, minBalance int64, numVa Accounts: accounts, Validators: validators, Params: &GenesisParams{ - GlobalPermissions: ptypes.NewDefaultAccountPermissions(), + GlobalPermissions: &defaultPerms, }, }) s0.Save() diff --git a/state/tx_cache.go b/state/tx_cache.go index c1d8da075..ddf78be9d 100644 --- a/state/tx_cache.go +++ b/state/tx_cache.go @@ -165,7 +165,7 @@ func toVMAccount(acc *ac.Account) *vm.Account { Code: acc.Code, // This is crazy. Nonce: int64(acc.Sequence), StorageRoot: LeftPadWord256(acc.StorageRoot), - Permissions: acc.Permissions.Copy(), + Permissions: acc.Permissions, // Copy Other: acc.PubKey, } } @@ -190,7 +190,7 @@ func toStateAccount(acc *vm.Account) *ac.Account { Code: acc.Code, Sequence: int(acc.Nonce), StorageRoot: storageRoot, - Permissions: acc.Permissions, + Permissions: acc.Permissions, // Copy } } diff --git a/vm/snative.go b/vm/snative.go index 18f997e3e..863c0a181 100644 --- a/vm/snative.go +++ b/vm/snative.go @@ -224,9 +224,9 @@ func (e ErrInvalidPermission) Error() string { // Checks if a permission flag is valid (a known base chain or snative permission) func ValidPermN(n ptypes.PermFlag) bool { - if n > ptypes.TopBasePermission && n < ptypes.FirstSNativePerm { + if n > ptypes.TopBasePermFlag && n < ptypes.FirstSNativePermFlag { return false - } else if n > ptypes.TopSNativePermission { + } else if n > ptypes.TopSNativePermFlag { return false } return true diff --git a/vm/test/vm_test.go b/vm/test/vm_test.go index 00ba6de8b..b491071be 100644 --- a/vm/test/vm_test.go +++ b/vm/test/vm_test.go @@ -10,16 +10,22 @@ import ( . "github.com/tendermint/tendermint/common" "github.com/tendermint/tendermint/events" + ptypes "github.com/tendermint/tendermint/permission/types" "github.com/tendermint/tendermint/types" . "github.com/tendermint/tendermint/vm" ) func newAppState() *FakeAppState { - return &FakeAppState{ + fas := &FakeAppState{ accounts: make(map[string]*Account), storage: make(map[string]Word256), logs: nil, } + // For default permissions + fas.accounts[ptypes.GlobalPermissionsAddress256.String()] = &Account{ + Permissions: ptypes.DefaultAccountPermissions, + } + return fas } func newParams() Params { diff --git a/vm/types.go b/vm/types.go index e0aa45206..6280ebc2b 100644 --- a/vm/types.go +++ b/vm/types.go @@ -17,7 +17,7 @@ type Account struct { StorageRoot Word256 Other interface{} // For holding all other data. - Permissions *ptypes.AccountPermissions + Permissions ptypes.AccountPermissions } func (acc *Account) String() string { diff --git a/vm/vm.go b/vm/vm.go index 8087443c6..16f9dc1b1 100644 --- a/vm/vm.go +++ b/vm/vm.go @@ -60,9 +60,6 @@ type VM struct { callDepth int evc events.Fireable - - perms bool // permission checking can be turned off - snative bool // access to snatives } func NewVM(appState AppState, params Params, origin Word256, txid []byte) *VM { @@ -80,25 +77,17 @@ func (vm *VM) SetFireable(evc events.Fireable) { vm.evc = evc } -// to allow calls to native DougContracts (off by default) -func (vm *VM) EnableSNatives() { - vm.snative = true -} - -// run permission checks before call and create -func (vm *VM) EnablePermissions() { - vm.perms = true -} - -// XXX: it is the duty of the contract writer to call known permissions +// CONTRACT: it is the duty of the contract writer to call known permissions // we do not convey if a permission is not set // (unlike in state/execution, where we guarantee HasPermission is called // on known permissions and panics else) +// If the perm is not defined in the acc nor set by default in GlobalPermissions, +// prints a log warning and returns false. func HasPermission(appState AppState, acc *Account, perm ptypes.PermFlag) bool { v, err := acc.Permissions.Base.Get(perm) if _, ok := err.(ptypes.ErrValueNotSet); ok { if appState == nil { - fmt.Printf("\n\n***** Unknown permission %b! ********\n\n", perm) + log.Warn(Fmt("\n\n***** Unknown permission %b! ********\n\n", perm)) return false } return HasPermission(nil, appState.GetAccount(ptypes.GlobalPermissionsAddress256), perm) @@ -125,8 +114,9 @@ func (vm *VM) Call(caller, callee *Account, code, input []byte, value int64, gas } }() + // SNATIVE ACCESS // if code is empty, callee may be snative contract - if vm.snative && len(code) == 0 { + if len(code) == 0 { if snativeContract, ok := RegisteredSNativeContracts[callee.Address]; ok { output, err = snativeContract(vm.appState, caller, input) if err != nil { @@ -135,6 +125,7 @@ func (vm *VM) Call(caller, callee *Account, code, input []byte, value int64, gas return } } + // SNATIVE ACCESS END if err = transfer(caller, callee, value); err != nil { *exception = err.Error() @@ -708,7 +699,7 @@ func (vm *VM) call(caller, callee *Account, code, input []byte, value int64, gas dbg.Printf(" => %v\n", log) case CREATE: // 0xF0 - if vm.perms && !HasPermission(vm.appState, callee, ptypes.CreateContract) { + if !HasPermission(vm.appState, callee, ptypes.CreateContract) { return nil, ErrPermission{"create_contract"} } contractValue := stack.Pop64() @@ -736,7 +727,7 @@ func (vm *VM) call(caller, callee *Account, code, input []byte, value int64, gas } case CALL, CALLCODE: // 0xF1, 0xF2 - if vm.perms && !HasPermission(vm.appState, callee, ptypes.Call) { + if !HasPermission(vm.appState, callee, ptypes.Call) { return nil, ErrPermission{"call"} } gasLimit := stack.Pop64() @@ -782,12 +773,12 @@ func (vm *VM) call(caller, callee *Account, code, input []byte, value int64, gas ret, err = vm.Call(callee, callee, acc.Code, args, value, gas) } else { if acc == nil { - if _, ok := RegisteredSNativeContracts[addr]; vm.snative && ok { + if _, ok := RegisteredSNativeContracts[addr]; ok { acc = &Account{Address: addr} } else { // if we have not seen the account before, create it // so we can send funds - if vm.perms && !HasPermission(vm.appState, caller, ptypes.CreateAccount) { + if !HasPermission(vm.appState, caller, ptypes.CreateAccount) { return nil, ErrPermission{"create_account"} } acc = &Account{Address: addr}