4. Foundations
Computation takes place in a context that determines what
sorts of events can and cannot occur; this context can be viewed as the
foundation of the rest of the system. Computational markets will require
foundations that permit and forbid the right sorts of events. To simplify this
discussion, the following explores foundations that provide for a basic
uniformity in the nature of objects and their interactions despite differences
in complexity and scale. In real systems, uniform foundations will ease the
process of changing scale-dependent decisions and will make possible a unified
set of conceptual and software tools spanning different scales.
It should be emphasized, however, that implementation of
an agoric system will not demand adoption of a standard programming language.
So long as certain constraints are met at the interfaces between objects coded
by different parties, the language used inside an object can be freely chosen.
The necessary constraints can be imposed by either the language or the
operating system.
Computational foundations are frequently expressed in the
form of programming language or operating system designs. Programming languages
have evolved chiefly to provide abstractions for organizing computation on a
small scale-the computation occurring inside a single machine and serving a
single user. This has unfortunately led many programming lan- guage designers
to make decisions (such as providing for global variables or access to
arbitrary memory addresses) that make these languages unsuitable for organizing
computation on a very large scale. The Actor languages, Argus, the concurrent
logic programming languages (such as FCP), and the Mach operating system are
examples of systems which have been designed to be extensible to large, open
systems. These are covered in this book respectively in [II], [III], [IV], [V], and [VI]. All these projects have arrived at broadly
similar models of computation from different directions, suggesting that their
common elements will be of general value. This section briefly outlines some of
the properties they share-properties which seem important for the implemention
of computational markets.
4.1. Information and access
As indicated in Figure 2, the systems
capable of supporting open computation all share support for the encapsulation
and communication of information and access. Communication of information is
fundamental to computation that involves more than a single object.
Encapsulation of information involves separating internal state and
implementation from external behavior, preventing one object from examining or
tampering with the contents of another.
In conventional practice, encapsulation of information
increases modularity and conceptual clarity during design, a feature of
considerable value. In agoric systems, though, secure encapsulation will be
essential during operation. Without security against examination, theft of
proprietary information would be rampant, and the rewards for the creation of
valuable code and information would be reduced or destroyed. Without security
against tampering, objects could not trust each other's future behavior, or
even their own. Encapsulation provides a sphere within which an object may act
with complete control and predictability.
Encapsulation and communication of access-capability
security-ensures that the ability to communicate with an object can only be
obtained in certain ways, such as through deliberate communication. With
capability security, object A can get access to object B only by:
- (1) being born with it, when object A's creator already
has that access;
- (2) receiving it in a message (from an object that
already has that access); or
- (3) being the creator of object B.
Capability security is a common foundation for protection
in operating systems. It appears to be a flexible and general mechanism able to
support a wide variety of policies for providing access protection. In an open
system without capability security, every object would have to verify the
nature and legitimacy of every message it received, imposing unacceptable
overhead on small, simple objects. With capability security, simple objects can
"assume" that their messages come from legitimate sources, because their
creators can implement policies that limit access to trusted parties.
Together, the above properties yield security while
preserving flexibility. Despite the Turing-equivalence of most programming
languages, they can nevertheless differ formally and absolutely in their
ability to provide for security [25]. How can
this be, if one can write an interpreter for a secure language in an insecure
one?
Turing-equivalence describes the abilities of a
system, but security rests on inabilities-on the inability to violate certain
rules. Adding an interpreter on top of a system cannot subtract abilities from
the system itself (even if the interpreted language consists of nothing but
inabilities, as can easily be arranged). Thus, adding interpreters cannot
establish the inabilities needed for security.
The question is not "what functions can be computed?", but
"given that I am a computational object, what is my relationship to an already
populated computational environment?". Let us call a set of computational
objects coded in an insecure programming language "reference level objects",
and those which exist on top of a reference-level interpreter "interpreted
objects". If the interpreter implements a secure language, then the interpreted
objects are protected from each other. Reference level objects, however, can
simply ignore the interpreter and wreak havoc on the interpreted objects.
4.2. Ownership and trade
As software systems have evolved toward greater
modularity, encapsulation of information and access have become more clean,
uniform, and reliable. As has been discussed, encapsulation in software serves
the same crucial function as property rights in human affairs: it establishes
protected spheres in which entities can plan the use of their resources free of
interference from unpredictable external influences. This enables entities to
plan and act despite the limited, local nature of most knowledge; it thus
permits more effective use of divided knowledge, aiding the division of labor.
The value of protected spheres and local knowledge has thus far been the sole
motivation for giving software modules "property rights" through
encapsulation.
In economic systems, property rights also enable economic
entities to accumulate and control the results of their efforts, providing the
basis for an incentive system having the desirable evolutionary properties
outlined in [I]. In agoric systems, encapsulation
will begin to serve this function as well.
Agoric systems also require the encapsulation and
communication of computational resources, such as a memory block or a processor
time slice. This prevents the evolution of parasitic objects [I], confines the costs of inefficiency to
inefficient objects and their customers, and (in suitable implementations)
makes performance information available locally.
Encapsulation and communication of resources correspond
to ownership and voluntary transfer, the basis of trade.
A familiar systems programming construct which violates
encapsulation of resources is the round-robin scheduler. In such a scheduler,
the amount of processing power allocated to a process depends simply on the
number of other processes. The processing power allocated to a given process
will be reduced whenever some other process decides to spawn yet more
processes. Under a round-robin scheduler, the processor is treated as a
commons; given a diversity of interests, the usual tragedy is to be expected
[26].
Artsy's paper on "The Design of Fully Open Computing
Systems" (FOCS) [22] discusses an approach for
an operating system design having the desirable properties specified above.
Artsy's use of the term "fully open computing systems" corresponds to what
would here be termed "extreme separation of mechanism and policy", where the
mechanism is the support of protected transfer of ownership and the
verification of ownership on access. All other resource allocation is then
provided as user-level policy. Thus, schedulers and memory allocators are
completely outside the secure operating system kernel and operate via an
ownership-and-trade model. One can, for example, own and trade time-slices of a
particular processor. Scheduling is performed at the user level by exchanging
such commodities.
Starting from direct ownership of physical computational
resources, more abstract models of ownership can be built. For example, a
deadline scheduler can be viewed as follows: When a task is to be scheduled in
a hard real-time application (i.e., one that must meet real-time deadlines), it
should be known beforehand how long it will take and by what time it must be
done. When a process wishes to insure that it will be able to schedule a set of
such tasks, it can purchase "abstract future time slices"-not specific time
slices, but rights to a time slice of a certain duration within a certain
period. Since this gives the seller of time slices greater flexibility with
respect to other clients, such time slices should cost less than concrete ones.
This is like a futures market, but with guaranteed availability-an honest
seller of time slices will not obligate himself to sell time slices he may not
be able to get. (See also [27].)
4.3. Resource ownership and performance modularity
The activity of a running program may be analyzed in terms
of competence and performance. Competence refers to what a
program can do given sufficient resources, but without explicit consideration
of these resources. Competence includes issues of safety-what the
program will not do, and liveness-whether the program will eventually do
what it is supposed to, or will instead infinitely loop or deadlock.
Performance refers to the resources the program will use, the efficiency with
which it will use them, and the time it will take to produce results-precisely
those issues ignored by competence. Both these issues have been the subject of
formal analysis: the competence aspects of a programming language may be
formalized as a programming language semantics and used to analyze safety
properties via proofs of partial correctness and liveness properties via proofs
of termination. The performance aspects of a program may be formally analyzed
via complexity theory and proofs of response time (for real-time
programming).
Formalization alone, however, is insufficient for dealing
with these issues in large programs-a complex non-modular program in a
formalized language will often resist formal (or informal) validation of many
important properties; modularity is needed to make analysis tractable.
Modularization proceeds by separating interface from implementation, allowing
concern with what a module does to be somewhat decoupled from concern with how
it does it. Object-oriented programming and abstract data types aid
modularization of competence issues, with message protocols serving as an
abstract interface for competence effects [28].
Similarly, computational markets will aid modularization of performance issues,
with prices serving as an abstract interface for resource costs.
4.4. Currency
For a broad market to emerge from these foundations, a
system must provide for ownership and trade not only of basic computational
resources, but also of virtual, user defined resources. Such resources can
serve as tokens for establishing a system of currency. Public key
communications systems [29] enable implemention
of a secure banking system; within a mutually trusted hardware subsystem,
capability-based security plus unforgeable unique identifiers are sufficient
for establishing a public key system without resorting to encryption [25].
Accounting mechanisms have been used in software to some
extent. Old time-sharing systems are one of the more familiar models-a fact
which may raise grave concerns about the desirability of agoric systems. But
using an agoric system would not mean a return to the bad old days of begging
for a grant of hundreds of dollars of computer time and storage to edit a
medium-sized document late at night, or to perform some now-inexpensive
computation. The cost of computers has fallen. It will continue to fall, and
personal computers will continue to spread. Aside from overhead (which can be
made small), accounting for the costs of computation will not make computation
more expensive. Making human beings pay for computer time is not the goal of
computational markets.
In agoric systems, objects will charge each other and the
machine will charge the objects. Given low enough communications costs and the
right sorts of demand, a personal computer could earn money for its owner by
serving others, instead of remaining idle. A machine's owner need not pay to
use it, since the internal charges and revenues all balance. In a stand-alone
computer, currency will simply circulate, incurring a computational overhead
but providing internal accounting information which can guide internal
decisions.
Inside one machine, one could have the foundations
establish an official currency system. No secure way has yet been found to do
so between mutually distrustful machines on a network without relying on
mutually-trusted, third-party machines serving as banks. In accord with the
goal of uniformity, such banks are here suggested as the general model for
transfer of currency [25,30]. These banks can maintain accounts for two
parties; when party A transfers money to party B, the bank can verify for B
that the money has been transferred. (The cost of verification provides an
incentive for A and B to establish enough trust to make frequent verification
unnecessary.) In this model, it is unnecessary and perhaps impossible to
establish any one currency as standard. There will instead be a variety of
local currencies with exchange rates among them; it has been argued that this
will result in greater monetary stability, and hence in a more efficient
market, than one based on a single currency [31].
4.5. Open problems
At the foundational level, many open issues remain. Actors
and FCP seem to be clean, simple open-systems programming languages, but they
have no evident mechanism for dealing with machine failure. Argus is an
open-systems language able to deal with this problem, but only by directly
providing distributed abortable transactions as a basic mechanism. While such
transactions provide much leverage, they are quite complex. A promising line of
investigation is the design of a language having the simplicity of Actors or
FCP, but which provides mechanisms for failure-handling that enable
user-level policy to support Argus-style transactions. Even more
satisfying than such a design would be a demonstration that Actors or FCP
already have sufficient mechanism.
More central to agoric systems is adequate resource
accounting. There is as yet no open-systems language which provides for
ownership and trade of basic computational resources while preserving semantic
uniformity and supporting the emergence of charging and prices. It seems this
has been accomplished in the realm of operating systems design [22], but unfortunately in a way which is not yet
amenable to distributed systems. It would be exciting to apply Artsy's work to
open-systems oriented operating systems like Mach [IV].
|