package vm import ( "fmt" . "github.com/tendermint/go-common" ptypes "github.com/tendermint/tendermint/permission/types" ) // TODO: ABI //------------------------------------------------------------------------------------------------ // Registered SNative contracts func registerSNativeContracts() { registeredNativeContracts[LeftPadWord256([]byte("has_base"))] = hasBasePerm registeredNativeContracts[LeftPadWord256([]byte("set_base"))] = setBasePerm registeredNativeContracts[LeftPadWord256([]byte("unset_base"))] = unsetBasePerm registeredNativeContracts[LeftPadWord256([]byte("set_global"))] = setGlobalPerm registeredNativeContracts[LeftPadWord256([]byte("has_role"))] = hasRole registeredNativeContracts[LeftPadWord256([]byte("add_role"))] = addRole registeredNativeContracts[LeftPadWord256([]byte("rm_role"))] = rmRole } //----------------------------------------------------------------------------- // snative are native contracts that can access and modify an account's permissions // TODO: catch errors, log em, return 0s to the vm (should some errors cause exceptions though?) func hasBasePerm(appState AppState, caller *Account, args []byte, gas *int64) (output []byte, err error) { if !HasPermission(appState, caller, ptypes.HasBase) { return nil, ErrInvalidPermission{caller.Address, "has_base"} } if len(args) != 2*32 { return nil, fmt.Errorf("hasBasePerm() takes two arguments (address, permFlag)") } addr, permNum := returnTwoArgs(args) vmAcc := appState.GetAccount(addr) if vmAcc == nil { return nil, fmt.Errorf("Unknown account %X", addr) } permN := ptypes.PermFlag(Uint64FromWord256(permNum)) // already shifted if !ValidPermN(permN) { return nil, ptypes.ErrInvalidPermission(permN) } var permInt byte if HasPermission(appState, vmAcc, permN) { permInt = 0x1 } else { permInt = 0x0 } dbg.Printf("snative.hasBasePerm(0x%X, %b) = %v\n", addr.Postfix(20), permN, permInt) return LeftPadWord256([]byte{permInt}).Bytes(), nil } func setBasePerm(appState AppState, caller *Account, args []byte, gas *int64) (output []byte, err error) { if !HasPermission(appState, caller, ptypes.SetBase) { return nil, ErrInvalidPermission{caller.Address, "set_base"} } if len(args) != 3*32 { return nil, fmt.Errorf("setBase() takes three arguments (address, permFlag, permission value)") } addr, permNum, perm := returnThreeArgs(args) vmAcc := appState.GetAccount(addr) if vmAcc == nil { return nil, fmt.Errorf("Unknown account %X", addr) } permN := ptypes.PermFlag(Uint64FromWord256(permNum)) if !ValidPermN(permN) { return nil, ptypes.ErrInvalidPermission(permN) } permV := !perm.IsZero() if err = vmAcc.Permissions.Base.Set(permN, permV); err != nil { return nil, err } appState.UpdateAccount(vmAcc) dbg.Printf("snative.setBasePerm(0x%X, %b, %v)\n", addr.Postfix(20), permN, permV) return perm.Bytes(), nil } func unsetBasePerm(appState AppState, caller *Account, args []byte, gas *int64) (output []byte, err error) { if !HasPermission(appState, caller, ptypes.UnsetBase) { return nil, ErrInvalidPermission{caller.Address, "unset_base"} } if len(args) != 2*32 { return nil, fmt.Errorf("unsetBase() takes two arguments (address, permFlag)") } addr, permNum := returnTwoArgs(args) vmAcc := appState.GetAccount(addr) if vmAcc == nil { return nil, fmt.Errorf("Unknown account %X", addr) } permN := ptypes.PermFlag(Uint64FromWord256(permNum)) if !ValidPermN(permN) { return nil, ptypes.ErrInvalidPermission(permN) } if err = vmAcc.Permissions.Base.Unset(permN); err != nil { return nil, err } appState.UpdateAccount(vmAcc) dbg.Printf("snative.unsetBasePerm(0x%X, %b)\n", addr.Postfix(20), permN) return permNum.Bytes(), nil } func setGlobalPerm(appState AppState, caller *Account, args []byte, gas *int64) (output []byte, err error) { if !HasPermission(appState, caller, ptypes.SetGlobal) { return nil, ErrInvalidPermission{caller.Address, "set_global"} } if len(args) != 2*32 { return nil, fmt.Errorf("setGlobal() takes two arguments (permFlag, permission value)") } permNum, perm := returnTwoArgs(args) vmAcc := appState.GetAccount(ptypes.GlobalPermissionsAddress256) if vmAcc == nil { PanicSanity("cant find the global permissions account") } permN := ptypes.PermFlag(Uint64FromWord256(permNum)) if !ValidPermN(permN) { return nil, ptypes.ErrInvalidPermission(permN) } permV := !perm.IsZero() if err = vmAcc.Permissions.Base.Set(permN, permV); err != nil { return nil, err } appState.UpdateAccount(vmAcc) dbg.Printf("snative.setGlobalPerm(%b, %v)\n", permN, permV) return perm.Bytes(), nil } func hasRole(appState AppState, caller *Account, args []byte, gas *int64) (output []byte, err error) { if !HasPermission(appState, caller, ptypes.HasRole) { return nil, ErrInvalidPermission{caller.Address, "has_role"} } if len(args) != 2*32 { return nil, fmt.Errorf("hasRole() takes two arguments (address, role)") } addr, role := returnTwoArgs(args) vmAcc := appState.GetAccount(addr) if vmAcc == nil { return nil, fmt.Errorf("Unknown account %X", addr) } roleS := string(role.Bytes()) var permInt byte if vmAcc.Permissions.HasRole(roleS) { permInt = 0x1 } else { permInt = 0x0 } dbg.Printf("snative.hasRole(0x%X, %s) = %v\n", addr.Postfix(20), roleS, permInt > 0) return LeftPadWord256([]byte{permInt}).Bytes(), nil } func addRole(appState AppState, caller *Account, args []byte, gas *int64) (output []byte, err error) { if !HasPermission(appState, caller, ptypes.AddRole) { return nil, ErrInvalidPermission{caller.Address, "add_role"} } if len(args) != 2*32 { return nil, fmt.Errorf("addRole() takes two arguments (address, role)") } addr, role := returnTwoArgs(args) vmAcc := appState.GetAccount(addr) if vmAcc == nil { return nil, fmt.Errorf("Unknown account %X", addr) } roleS := string(role.Bytes()) var permInt byte if vmAcc.Permissions.AddRole(roleS) { permInt = 0x1 } else { permInt = 0x0 } appState.UpdateAccount(vmAcc) dbg.Printf("snative.addRole(0x%X, %s) = %v\n", addr.Postfix(20), roleS, permInt > 0) return LeftPadWord256([]byte{permInt}).Bytes(), nil } func rmRole(appState AppState, caller *Account, args []byte, gas *int64) (output []byte, err error) { if !HasPermission(appState, caller, ptypes.RmRole) { return nil, ErrInvalidPermission{caller.Address, "rm_role"} } if len(args) != 2*32 { return nil, fmt.Errorf("rmRole() takes two arguments (address, role)") } addr, role := returnTwoArgs(args) vmAcc := appState.GetAccount(addr) if vmAcc == nil { return nil, fmt.Errorf("Unknown account %X", addr) } roleS := string(role.Bytes()) var permInt byte if vmAcc.Permissions.RmRole(roleS) { permInt = 0x1 } else { permInt = 0x0 } appState.UpdateAccount(vmAcc) dbg.Printf("snative.rmRole(0x%X, %s) = %v\n", addr.Postfix(20), roleS, permInt > 0) return LeftPadWord256([]byte{permInt}).Bytes(), nil } //------------------------------------------------------------------------------------------------ // Errors and utility funcs type ErrInvalidPermission struct { Address Word256 SNative string } func (e ErrInvalidPermission) Error() string { return fmt.Sprintf("Account %X does not have permission snative.%s", e.Address.Postfix(20), e.SNative) } // Checks if a permission flag is valid (a known base chain or snative permission) func ValidPermN(n ptypes.PermFlag) bool { if n > ptypes.TopPermFlag { return false } return true } // CONTRACT: length has already been checked func returnTwoArgs(args []byte) (a Word256, b Word256) { copy(a[:], args[:32]) copy(b[:], args[32:64]) return } // CONTRACT: length has already been checked func returnThreeArgs(args []byte) (a Word256, b Word256, c Word256) { copy(a[:], args[:32]) copy(b[:], args[32:64]) copy(c[:], args[64:96]) return }