Holistic Specifications for Robust Programs

Functional specifications describe what program components can do: the sufficient conditions to invoke components’ operations. They allow us to reason about the use of components in a closed world setting, where components interact with known client code, and where the client code must establish the appropriate pre-conditions before calling into a component. Sufficient conditions are not enough to reason about the use of components in an open world setting, where components interact with external code, possibly of unknown provenance, and where components may evolve over time. In this open world setting, we must also consider the necessary conditions, i. e. what are the conditions without which an effect will not happen. In this paper we propose the \documentclass[12pt]{minimal} \usepackage{amsmath} \usepackage{wasysym} \usepackage{amsfonts} \usepackage{amssymb} \usepackage{amsbsy} \usepackage{mathrsfs} \usepackage{upgreek} \setlength{\oddsidemargin}{-69pt} \begin{document}$${\mathcal {C}}$$\end{document}hainmail specification language for writing holistic specifications that focus on necessary conditions (as well as sufficient conditions). We give a formal semantics for \documentclass[12pt]{minimal} \usepackage{amsmath} \usepackage{wasysym} \usepackage{amsfonts} \usepackage{amssymb} \usepackage{amsbsy} \usepackage{mathrsfs} \usepackage{upgreek} \setlength{\oddsidemargin}{-69pt} \begin{document}$${\mathcal {C}}$$\end{document}hainmail, and discuss several examples. The core of \documentclass[12pt]{minimal} \usepackage{amsmath} \usepackage{wasysym} \usepackage{amsfonts} \usepackage{amssymb} \usepackage{amsbsy} \usepackage{mathrsfs} \usepackage{upgreek} \setlength{\oddsidemargin}{-69pt} \begin{document}$${\mathcal {C}}$$\end{document}hainmail has been mechanised in the Coq proof assistant.


Introduction
Software guards our secrets, our money, our intellectual property, our reputation [47]. We entrust personal and corporate information to software which works in an open world, where it interacts with third party software of unknown provenance, possibly buggy and potentially malicious.
This means we need our software to be robust: to behave correctly even if used by erroneous or malicious third parties. We expect that our bank will only make payments from our account if instructed by us, or by somebody we have authorised, that space on a web given to an advertiser will not be used to obtain access to our bank details [43], or that a concert hall will not book the same seat more than once.
While language mechanisms such as constants, invariants, object capabilities [40], and ownership [15] make it possible to write robust programs, they cannot ensure that programs are robust. Ensuring robustness is difficult because it means different things for different systems: perhaps that critical operations should only be invoked with the requisite authority; perhaps that sensitive personal information should not be leaked; or perhaps that a resource belonging to one user should not be consumed by another. To ensure robustness, we need ways to specify what robustness means for a particular program, and ways to demonstrate that the particular program adheres to its specific robustness requirements.
Consider the code snippets from Fig. 1. Objects of class Safe hold a treasure and a secret, and only the holder of the secret can remove the treasure from the safe.  We show the code in two versions; both have the same method take, and the second version has an additional method set. We assume a dynamically typed language (so that our results are applicable to the statically as well as the dynamically typed setting); 3 that fields are private in the sense of Java (i.e. only methods of that class may read or write these fields); and that addresses are unforgeable (so there is no way to guess a secret). A classical Hoare triple describing the behaviour of take would be: (ClassicSpec) method take(scr) PRE: this:Safe POST: scr=this.secretpre −→ this.treasure=null ∧ scr =this.secretpre −→ ∀s:Safe. s.treasure=s.treasurepre (ClassicSpec) expresses that knowledge of the secret is sufficient to remove the treasure, and that take cannot remove the treasure unless the secret is provided. But it cannot preclude that Safe -or some other class, for that matter -contains more methods which might make it possible to remove the treasure without knowledge of the secret. This is the problem with the second version of Safe: it satisfies (ClassicSpec), but is not robust, as it is possible to overwrite the secret of the Safe and then use it to remove the treasure. To express robustness requirements, we introduce holistic specifications, and require that: (HolisticSpec) mandates that for any safe s whose treasure is not null, if some time in the future its treasure were to become null, then at least one external object (i.e. an object whose class is not Safe) in the current configuration has direct access to s's secret. This external object need not have caused the change in s.treasure but it would have (transitively) passed access to the secret which ultimately did cause that change. Both classes in Fig. 1 satisfy (ClassicSpec), but the second version does not satisfy (HolisticSpec).
In this paper we propose Chainmail, a specification language to express holistic specifications. The design of Chainmail was guided by the study of a sequence of examples from the object-capability literature and the smart contracts world: the membrane [18], the DOM [21,59], the Mint/Purse [40], the Escrow [19], the DAO [13,16] and ERC20 [61]. As we worked through the examples, we found a small set of language constructs that let us write holistic specifications across a range of different contexts. Chainmail extends traditional program specification languages [31,37] with features which talk about: Permission: Which objects may have access to which other objects; this is central since access to an object grants access to the functions it provides. Control: Which objects called functions on other objects; this is useful in identifying the causes of certain effects -eg funds can only be reduced if the owner called a payment function. Time: What holds some time in the past, the future, and what changes with time, Space: Which parts of the heap are considered when establishing some property, or when performing program execution; a concept related to, but different from, memory footprints and separation logics, Viewpoint: Which objects and which configurations are internal to our component, and which are external to it; a concept related to the open world setting.
While many individual features of Chainmail can be found in other work, their power and novelty for specifying open systems lies in their careful combination. The contributions of this paper are: the design of the holistic specification language Chainmail, the semantics of Chainmail, a Coq mechanisation of the core of Chainmail.
The rest of the paper is organised as follows: Section 2 gives an example from the literature which we will use to elucidate key points of Chainmail. 3 presents the Chainmail specification language. Section 4 introduces the formal model underlying Chainmail, and then section 5 defines the semantics of Chainmail's assertions. Section 6 discusses our design, 7 considers related work, and section 8 concludes. We relegate key points of exemplar problems and various details to appendices which are available at [1].

Motivating Example: The Bank
As a motivating example, we consider a simplified banking application taken from the object capabilities literature [41]: Accounts belong to Banks and hold money (balances); with access to two Accounts of the same Bank one can transfer any amount of money from one to the other. This example has the advantage that it requires several objects and classes.
We will not show the code here (see appendix C), but suffice it to say that class Account has methods deposit(src, amt) and makeAccount(amt) (i.e. a method called deposit with two arguments, and a method called makeAccount with one argument). Similarly, Bank has method newAccount(amt). Moreover, deposit requires that the receiver and first argument (this and src) are Accounts and belong to the same bank, that the second argument (amt) is a number, and that src's balance is at least amt. If this condition holds, then amt gets transferred from src to the receiver. The function makeNewAccount returns a fresh Account with the same bank, and transfers amt from the receiver Account to the new Account. Finally, the function newAccount when run by a Bank creates a new Account with corresponding amount of money in it. 4 It is not difficult to give formal specifications of these methods in terms of pre-and post-conditions. However, what if the bank provided a steal method that emptied out every account in the bank into a thief's account? The critical problem is that a bank implementation including a steal method could meet the functional specifications of deposit, makeAccount, and newAccount, and still allow the clients' money to be stolen.
One obvious solution would be to adopt a closed-world interpretation of specifications: we interpret functional specifications as exact in the sense that only implementations that meet the functional specification exactly, with no extra methods or behaviour, are considered as suitable implementations of the functional specification. The problem is that this solution is far too strong: it would for example rule out a bank that during software maintenance was given a new method count that simply counted the number of deposits that had taken place, or a method notify to enable the bank to occasionally send notifications to its customers.
What we need is some way to permit bank implementations that send notifications to customers, but to forbid implementations of steal. The key here is to capture the (implicit) assumptions underlying the design of the banking application. We provide additional specifications that capture those assumptions. The following three informal requirements prevent methods like steal: 1. An account's balance can be changed only if a client calls the deposit method with the account as the receiver or as an argument. 2. An account's balance can be changed only if a client has access to that particular account. 3. The Bank/Account component does not leak access to existing accounts or banks.
Compared with the functional specification we have seen so far, these requirements capture necessary rather than sufficient conditions: Calling the deposit method to gain access to an account is necessary for any change to that account taking place. The function steal is inconsistent with requirement (1), as it reduces the balance of an Account without calling the function deposit. However, requirement (1) is not enough to protect our money. We need to (2) to avoid an Account's balance getting modified without access to the particular Account, and (3) to ensure that such accesses are not leaked.
We can express these requirements through Chainmail assertions. Rather than specifying the behaviour of particular methods when they are called, we write assertions that range across the entire behaviour of the Bank/Account module.
In the above and throughout the paper, we use an underscore (_) to indicate an existentially bound variable whose value is of no interest.
Assertion (1) says that if an account's balance changes (changes a.balance ), then there must be some client object o that called the deposit method with a as a receiver or as an argument ( o calls _.deposit( _) ).
Assertion (2) similarly constrains any possible change to an account's balance: If at some future point the balance changes (will changes ... ), and if this future change is observed with the state restricted to the objects from S (i.e. ... in S ), then at least one of these objects (o ∈ S) is external to the Bank/Account system (external o ) and has (direct) access to that account object ( o access a ). Notice that while the change in the balance happens some time in the future, the external object o has access to a in the current state. Notice also, that the object which makes the call to deposit described in (1), and the object which has access to a in the current state described in (2) need not be the same: It may well be that the latter passes a reference to a to the former (indirectly), which then makes the call to deposit.
It remains to think about how access to an Account may be obtained. This is the remit of assertion (3): It says that if at some time in the future of the state restricted to S, some object o which is external has access to some account a, and if a exists in the current state, then in the current state some object from S has access to a. Where o and o may, but need not, be the same object. And where o has to exist and have access to a in the current state, but o need not exist in the current state -it may be allocated later. Assertion (3) thus gives essential protection when dealing with foreign, untrusted code. When an Account is given out to untrusted third parties, assertion (3) guarantees that this Account cannot be used to obtain access to further Accounts.
A holistic specification for the bank account, then, would be a sufficient functional specification plus the necessary specifications (1)-(3) from above. This holistic specification permits an implementation of the bank that also provides count and notify methods, even though the specification does not mention either method. Critically, though, the Chainmail specification does not permit an implementation that includes a steal method.

Chainmail Overview
In this Section we give a brief and informal overview of some of the most salient features of Chainmaila full exposition appears in Section 5.
Example Configurations We will illustrate these features using the Bank/Account example from the previous Section. We use the runtime configurations σ 1 and σ 2 shown in the left and right diagrams in Figure 2. In both diagrams the rounded boxes depict objects: green for those from the Bank/Account component, and grey for the "external", "client" objects. The transparent green rectangle shows which objects are contained by the Bank/Account component. The object at 1 is a Bank, those at 2, 3 and 4 are Accounts, and those at 91, 92, 93 and 94 are "client" objects which belong to classes different from those from the Bank/Account module.
Each configuration represents one alternative implementation of the Bank object. Configuration σ 1 may arise from execution using a module M BA1 , where Account objects have a field myBank pointing to their Bank, and an integer field balancethe code can be found in appendix C Fig. 6. Configuration σ 2 may arise from execution using a module M BA2 , where Accounts have a myBank field, Bank objects have a ledger implemented though a sequence of Nodes, each of which has a field pointing to an Account, a field balance, and a field next -the code can be found in appendix C Figs. 9 and 7.   For the rest, assume variable identifiers b 1 , and a 2 -a 4 , and u 91 -u 94 denoting objects 1, 2-4, and 91-94 respectively for both σ 1 and σ 2 . That is, for i=1 or i=2, Classical Assertions talk about the contents of the local variables (i.e. the topmost stack frame), and the fields of the various objects (i.e. the heap). For example, the assertion a 2 .myBank=a 3 .myBank, says that a 2 and a 3 have the same bank. In fact, this assertion is satisfied in both σ 1 and σ 2 , written formally as ..., σ 1 |= a 2 .myBank = a 3 .myBank ..., σ 2 |= a 2 .myBank = a 3 .myBank. The term x:ClassId says that x is an object of class ClassId. For example ..., σ 1 |= a 2 .myBank : Bank. We support ghost fields [12,31], e.g. a 1 .balance is a physical field in σ 1 and a ghost field in σ 2 since in MBA2 an Account does not store its balance (as can be seen in appendix C Fig. 9). We also support the usual logical connectives, and so, we can express assertions such as ∀a.
Permission: Access Our first holistic assertion, x access y , asserts that object x has a direct reference to another object y: either one of x's fields contains a reference to y, or the receiver of the currently executing method is x, and y is one of the arguments or a local variable. For example: ..., σ 1 |= a 2 access b 1 If σ 1 were executing the method body corresponding to the call a 2 .deposit(a 3 ,360), then we would have ..., σ 1 |= a 2 access a 3 , Namely, during execution of deposit, the object at a 2 has access to the object at a 3 , and could, if the method body chose to, call a method on a 3 , or store a reference to a 3 in its own fields. Access is not symmetric, nor transitive: ..., σ 1 |= a 3 access a 2 , ..., σ 2 |= a 2 access * a 3 , ..., σ 2 |= a 2 access a 3 .

Control: Calls
The assertion x calls m.y( zs) holds in configurations where a method on object x makes a method call y.m(zs) -that is it calls method m with object y as the receiver, and with arguments zs. For example, ..., σ 3 |= x calls a 2 .deposit( a 3 , 360) . means that the receiver in σ 3 is x, and that a 2 .deposit(a 3 , 360) is the next statement to be executed.
Space: In The space assertion A in S establishes validity of A in a configuration restricted to the objects from the set S. For example, if object 94 is included in S 1 but not in S 2 , then we have ..., σ 1 |= (∃o. o access a 4 ) in S 1 ..., σ 1 |= (∃o. o access a 4 ) in S 2 . The set S in the assertion A in S is therefore not the footprint of A; it is more like the fuel [3] given to establish that assertion. Note that ..., σ |= A in S does not imply ..., σ |= A nor does it imply ..., σ |= A in S ∪ S . The other direction of the implication does not hold either.
Time: Next, Will, Prev, Was We support several operators from temporal logic: (next A , will A , prev A , and was A ) to talk about the future or the past in one or more steps. The assertion will A expresses that A will hold in one or more steps. For example, taking σ 4 to be similar to σ 2 , the next statement to be executed to be a 2 .deposit(a 3 , 360), and M BA2 ..., σ 4 |= a 2 .balance = 60, and that M BA2 ..., σ 4 |= a 4 .balance ≥ 360, then M BA2 ..., σ 4 |= will a 2 .balance = 420 . The internal module, M BA2 is needed for looking up the method body of deposit.
Viewpoint: -External The assertion external x expresses that the object at x does not belong to the module under consideration. For example, M AB2 ..., σ 2 |= external u 92 , M AB2 ..., σ 2 |= external a 2 , M AB2 ..., σ 2 |= external b 1 .ledger The internal module, M BA2 , is needed to judge which objects are internal or external.
Change and Authority: We have used changes ... in our Chainmail assertions in section 2, as in changes a.balance . Assertions that talk about change, or give conditions for change to happen are fundamental for security; the ability to cause change is called authority in [40]. We could encode change using the other features of Chainmail, namely, for any expression e: changes e ≡ ∃v.[ e = v ∧ next ¬(e = v) ]. and similarly for assertions.
Putting these together We now look at some composite assertions which use several features from above. The assertion below says that if the statement to be executed is a 2 .deposit(a 3 , 60), then the balance of a 2 will eventually change: M BA2 ..., σ 2 |= .. calls a 2 .deposit( a 3 , 60) −→ will changes a 2 .balance . Now look deeper into space assertions, A in S : They allow us to characterise the set of objects which have authority over certain effects (here A). In particular, the assertion will A in S requires two things: i) that A will hold in the future, and ii) that the objects which cause the effect which will make A valid, are included in S. Knowing who has, and who has not, authority over properties or data is a fundamental concern of robustness [40]. Notice that the authority is a set, rather than a single object: quite often it takes several objects in concert to achieve an effect.
Consider assertions (2) and (3) from the previous section. They both have the form "will A in S −→ P (S)", where P is some property over a set. These assertions say that if ever in the future A becomes valid, and if the objects involved in making A valid are included in S, then S must satisfy P . Such assertions can be used to restrict whether A will become valid. If we have some execution which only involves objects which do not satisfy P , then we know that the execution will not ever make A valid.
In summary, in addition to classical logical connectors and classical assertions over the contents of the heap and the stack, our holistic assertions draw from some concepts from object capabilities ( _ access _ for permission; _ calls _._( _) and changes _ for authority) as well as temporal logic (will A , was A and friends), and the relation of our spatial connective ( A in S ) with ownership and effect systems [60, 15,14].
The next two sections discuss the semantics of Chainmail. Section 4 contains an overview of the formal model and section 5 focuses on the most important part of Chainmail: assertions.

Overview of the Formal foundations
We now give an overview of the formal model for Chainmail. In section 4.1 we introduce the shape of the judgments used to give semantics to Chainmail, while in section 4.2 we describe the most salient aspects of an underlying programming language used in Chainmail.

Chainmail judgments
Having outlined the ingredients of our holistic specification language, the next question to ask is: When does a module M satisfy a holistic assertion A? More formally: when does M |= A hold?
Our answer has to reflect the fact that we are dealing with an open world, where M, our module, may be linked with arbitrary untrusted code. To model the open world, we consider pairs of modules, M M , where M is the module whose code is supposed to satisfy the assertion, and M is another module which exercises the functionality of M. We call our module M the internal module, and M the external module, which represents potential attackers or adversaries.
We can now answer the question: M |= A holds if for all further, potentially adversarial, modules M and in all runtime configurations σ which may be observed as arising from the execution of the code of M combined with that of M , the assertion A is satisfied. More formally, we define: The judgement M M , σ |= A means that assertion A is satisfied by M M and σ. As in traditional specification languages [31,37], satisfaction is judged in the context of a runtime configuration σ; but in addition, it is judged in the context of the internal and external modules. These are used to find abstract functions defining ghost fields as well as method bodies needed when judging validity of temporal assertions such as will _ .
We distinguish between internal and external modules. This has two uses: First, Chainmail includes the "external o " assertion to require that an object belongs to the external module, as in the Bank Account's assertion (2)

An underlying programming language, L oo
The meaning of Chainmail assertions is parametric with an underlying object-oriented programming language, with modules as repositories of code, classes with fields, methods and ghostfields, objects described by classes, a way to link modules into larger ones, and a concept of program execution. 5 We have developed L oo , a minimal such object-oriented language, which we outline in this section. We describe the novel aspects of L oo " and summarise the more conventional parts, relegating full, and mostly unsurprising, definitions to Appendix A, Modules are central to L oo , as they are to Chainmail. As modules are repositories of code, we adopt the common formalisation of modules as maps from class identifiers to class definitions, c.f. Appendix, Def. 11. We use the terms module and component in an analogous manner to class and object respectively. L oo is untyped -this has several reasons: Many popular programming languages are untyped. The external module might be untyped, and so it is more general to consider everything as untyped. Finally, a solution that works for an untyped language will also apply to a typed language;' the converse is not true.
Class definitions consist of field, method and ghost field declarations, c.f. Appendix, Def. 12. Method bodies are sequences of statements, which can be field read or field assignments, object creation, method calls, and return statements. Fields are private in the sense of C++: they can only be read or written by methods of the current class. This is enforced by the operational semantics, c.f. Fig. 4. We discuss ghost fields in the next section.
Runtime configurations, σ, contain all the usual information about execution snapshots: the heap, and a stack of frames. Each frame consists of a continuation, contn, describing the remaining code to be executed by the frame, and a map from variables to values. Values are either addresses or sets of addresses; the latter are needed to deal with assertions which quantify over sets of objects, as e.g. (1) and (2) from section 2. We define one-module execution through a judgment of the form M, σ ; σ in the Appendix, Fig. 4.
We define a module linking operator • so that M•M is the union of the two modules, provided that their domains are disjoint, c.f. Appendix, Def. 18. As we said in section 4.1, we distinguish between the internal and external module. We consider execution from the view of the external module, and treat execution of methods from the internal module as atomic. For this, we define two-module execution based on one-module execution as follows: Definition 1. Given runtime configurations σ, σ , and a module-pair M M we define execution where M is the internal, and M is the external module as below: if there exist n ≥ 2 and runtime configurations σ 1 , ... σ n , such that • σ=σ 1 , and σ n = σ .
Two-module execution is related to visible states semantics [45] as they both filter configurations, with the difference that in visible states semantics execution is unfiltered and configurations are only filtered when it comes to the consideration of class invariants while two-module execution filters execution. The lemma below says that linking is associative and commutative, and preserves both one-module and two-module execution.
Lemma 1 (Properties of linking). For any modules M, M , M , and M and runtime configurations σ, and σ we have: We can now answer the question as to which runtime configurations are pertinent when judging a module's adherence to an assertion. Initial configurations, are those whose heap have only one object, of class Object, and whose stack have one frame, with arbitrary continuation. Arising configurations are those that can be reached by two-module execution, starting from any initial configuration.
Definition 2 (Initial and Arising Configurations). are defined as follows: and there exists some address α, such that this φ =α, and dom(χ)=α, and

Assertions
Chainmail assertions (details in appendix B.3) consist of (pure) expressions e, comparisons between expressions, classical assertions about the contents of heap and stack, the usual logical connectives, as well as our holistic concepts. In this section we focus on the novel, holistic, features of Chainmail (permission, control, time, space, and viewpoint), as well as our wish to support some form of recursion while keeping the logic of assertions classical.

Satisfaction of Assertions -Access, Control, Space, Viewpoint
Permission expresses that an object has the potential to call methods on another object, and to do so directly, without help from any intermediary object. This is the case when the two objects are aliases, or the first object has a field pointing to the second object, or the first object is the receiver of the currently executing method and the second object is one of the arguments or a local variable. Interpretations of variables and paths, ... σ , are defined in the usual way (appendix Def. 15).

Definition 3 (Permission).
For any modules M, M , variables x and y, we define x σ and y σ are defined, and • x σ = y σ , or • x.f σ = y σ , for some field f, or • x σ = this σ and y σ = z σ , for some variable z and z appears in σ.contn.
In the last disjunct, where z is a parameter or local variable, we ask that z appears in the code being executed (σ.contn). This requirement ensures that variables which were introduced into the variable map in order to give meaning to existentially quantified assertions, are not considered.
Control expresses which object is the process of making a function call on another object and with what arguments. The relevant information is stored in the continuation (cont) on the top frame.
Thus, x calls y.m( z 1 , ...z n ) expresses the call y.m(z 1 , ...z n ) will be executed next, and that the caller is x.
Viewpoint is about whether an object is viewed as belonging to the internal mode; this is determined by the class of the object.
Definition 5 (Viewpoint). For any modules M, M , and variablex, we define Space is about asserting that some property A holds in a configuration whose objects are restricted to those from a give set S. This way we can express that the objects from the set S have authority over the assertion A. In order to define validity of A in S in a configuration σ, we first define a restriction operation, σ↓ S which restricts the objects from σ to only those from S.
Definition 6 (Restriction of Runtime Configurations). The restriction operator ↓ applied to a runtime configuration σ and a variable S is defined as follows: For example, if we take σ 2 from Fig.  2 in Section 2, and restrict it with some set S 4 such that S 4 σ2 = {91, 1, 2, 3, 4, 11}, then the restriction σ 2 ↓ S4 will look as on the right. Note in the diagram above the dangling pointers at objects 1, 11, and 91 -reminiscent of the separation of heaps into disjoint subheaps, as provided by the * operator in separation logic [53]. The difference is, that in separation logic, the separation is provided through the assertions, where A * A holds in any heap which can be split into disjoint χ and χ where χ satisfies A and χ satisfies A . That is, in A * A the split of the heap is determined by the assertions A and A and there is an implicit requirement of disjointness, while in σ↓ S the split is determined by S, and no disjointness is required.
We now define the semantics of A in S .
Definition 7 (Space). For any modules M, M , assertions A and variable S, we define: The set S in the assertion A in S is related to framing from implicit dynamic frames [57]: in an implicit dynamic frames assertion acc x.f * A, the frame x.f prescrives which locations may be used to determine validity of A. The difference is that frames are sets of locations (pairs of address and field), while our S-es are sets of addresses. More importantly, implicit dynamic frames assertions whose frames are not large enough are badly formed, while in our work, such assertions are allowed and may hold or not, e.g. M BA2 M , σ |= ¬ (∃n.a 2 .balance = n) in S 4 .

Satisfaction of Assertions -Time
To deal with time, we are faced with four challenges: a) validity of assertions in the future or the past needs to be judged in the future configuration, but using the bindings from the current one, b) the current configuration needs to store the code being executed, so as to be able to calculate future configurations, c) when considering the future, we do not want to observe configurations which go beyond the frame currently at the top of the stack, d) there is no "undo" operator to deterministically enumerate all the previous configurations.
Consider challenge a) in some more detail: the assertion will x.f = 3 is satisfied in the current configuration, σ 1 , if in some future configuration, σ 2 , the field f of the object that is pointed at by x in the current configuration (σ 1 ) has the value 3, that is, if x σ1 .f σ2 = 3, even if in that future configuration x denotes a different object (i.e. if x σ1 = x σ2 ). To address this, we define an auxiliary concept: the operator , where σ 1 σ 2 adapts the second configuration to the top frame's view of the former: it returns a new configuration whose stack comes from σ 2 but is augmented with the view from the top frame from σ 1 and where the continuation has been consistently renamed,. This allows us to interpret expressions in σ 2 but with the variables bound according to σ 1 ; e.g. we can obtain that value of x in configuration σ 2 even if x was out of scope in σ 2 .
That is, in the new frame φ 2 from above, we keep the same continuation as from σ 2 but rename all variables with fresh names zs , and combine the variable map β 1 from σ 1 with the variable map β 2 from σ 2 while avoiding names clashes through the renaming [zs → β 2 (zs 2 )]. The consistent renaming of the continuation allows the correct modelling of execution (as needed, for the semantics of nested time assertions, as e.g. in will x.f = 3 ∧ will x.f = 5 ).
Having addressed challenge a) we turn our attention to the remaining challenges: We address challenge b) by storing the remaining code to be executed in cntn in each frame. We address challenge c) by only taking the top of the frame when considering future executions. Finally, we address challenge d) by considering only configurations which arise from initial configurations, and which lead to the current configuration.
In general, will A in S is different from will A in S . Namely, in the former assertion, S must contain the objects involved in reaching the future configuration as well as the objects needed to then establish validity of A in that future configuration. In the latter assertion, S need only contain the objects needed to establish A in that future configuration. For example, revisit Fig. 2, and take S 1 to consist of objects 1, 2, 4, 93, and 94, and S 2 to consist of objects 1, 2, 4. Assume that σ 5 is like σ 1 , that the next call in σ 5 is a method on u 94 , whose body obtains the address of a 4 (by making a call on 93 to which it has access), and the address of a 2 (to which it has access), and then makes the call a 2 .deposit(a 4 , 360). Assume also that a 4 's balance is 380. Then M BA1 ..., σ 5 |= will changes a 2 .balance in S 1 M BA1 ..., σ 5 |= will changes a 2 .balance in S 2 M BA1 ..., σ 5 |= will changes a 2 .balance in S 2

Properties of Assertions
We define equivalence of assertions in the usual way: We present these exemplars as appendices [1]. Our design was also driven by work on other examples such as the membrane [18], the Mint/Purse [40], and Escrow [19,24].
Model We have constructed a Coq model [2] of the core of the Chainmail specification language, along with the underlying L oo language. Our formalism is organised as follows: 1. The L oo Language: a class based, object oriented language with mutable references. In the associated appendix (see Appendix G) we list and present the properties of Chainmail we have formalised in Coq. We have proven that Chainmail obeys much of the properties of classical logic. While we formalise most of the underlying semantics, we make several assumptions in our Coq formalism: (i) the law of the excluded middle, a property that is well known to be unprovable in constructive logics, and (ii) the equality of variable maps and heaps down to renaming. Coq formalisms often require fairly verbose definitions and proofs of properties involving variable substitution and renaming, and assuming equality down to renaming saves much effort.
More details of the formal foundations of Chainmail, and the model, are also in appendices [1]. . In general, these approaches assume a closed system, where modules can be trusted to cooperate. In this paper we aim to work in an open system where modules' invariants must be protected irrespective of the behaviour of the rest of the system.

Defensive Consistency
In an open world, we cannot rely on the kindness of strangers: rather we have to ensure our code is correct regardless of whether it interacts with friends or foes. Attackers "only have to be lucky once" while secure systems "have to be lucky always" [6]. Miller [39,40] defines the necessary approach as defensive consistency: "An object is defensively consistent when it can defend its own invariants and provide correct service to its well behaved clients, despite arbitrary or malicious misbehaviour by its other clients." Defensively consistent modules are particularly hard to design, to write, to understand, and to verify: but they make it much easier to make guarantees about systems composed of multiple components [46].
Object Capabilities and Sandboxes. Capabilities as a means to support the development of concurrent and distributed system were developed in the 60's by Dennis  : an object is defensively consistent when the addition of untrustworthy clients cannot cause well-behaved clients to be given incorrect service. Murray formalised defensive consistency very abstractly, over models of (concurrent) objectcapability systems in the process algebra CSP [29], without a specification language for describing effects, such as what it means for an object to provide incorrect service. Both Miller and Murray's definitions are intensional, describing what it means for an object to be defensively consistent.
Drossopoulou and Noble [22,48] have analysed Miller's Mint and Purse example [40] and discussed the six capability policies as proposed in [40]. In [23], they sketched a specification language, used it to specify the six policies from [40], showed that several possible interpretations were possible, and uncovered the need for another four further policies. They also sketched how a trust-sensitive example (the escrow exchange) could be verified in an open world [24]. Their work does not support the concepts of control, time, or space, as in Chainmail, but it offers a primitive expressing trust.
Swasey et al. [21] have deployed powerful theoretical techniques to address similar problems: They show how step-indexing, Kripke worlds, and representing objects as state machines with public and private transitions can be used to reason about object capabilities. Devriese have demonstrated solutions to a range of exemplar problems, including the DOM wrapper (replicated in our section F) and a mashup application. Their distinction between public and private transitions is similar to the distinction between internal and external objects.
More recently, Swasey et al.
[59] designed OCPL, a logic for object capability patterns, that supports specifications and proofs for object-oriented systems in an open world. They draw on verification techniques for security and information flow: separating internal implementations ("high values" which must not be exposed to attacking code) from interface objects ("low values" which may be exposed). OCPL supports defensive consistency (they use the term "robust safety" from the security community [7]) via a proof system that ensures low values can never leak high values to external attackers. This means that low values can be exposed to external code, and the behaviour of the system is described by considering attacks only on low values. They use that logic to prove a number of object-capability patterns, including sealer/unsealer pairs, the caretaker, and a general membrane.
Schaefer et al. [55] have recently added support for information-flow security using refinement to ensure correctness (in this case confidentiality) by construction. By enforcing encapsulation, all these approaches share similarity with techniques such as ownership types [15,50], which also protect internal implementation objects from accesses that cross encapsulation boundaries. Banerjee and Naumann demonstrated that these systems enforce representation independence (a property close to "robust safety") some time ago [4].
Chainmail differs from Swasey, Schaefer's, and Devriese's work in a number of ways: They are primarily concerned with mechanisms that ensure encapsulation (aka confinement) while we abstract away from any mechanism via the external predicate. They use powerful mathematical techniques which the users need to understand in order to write their specifications, while the Chainmail users only need to understand first order logic and the holistic operators presented in this paper. Finally, none of these systems offer the kinds of holistic assertions addressing control flow, change, or temporal operations that are at the core of Chainmail's approach.
Scilla [56] is a minimalistic typed functional language for writing smart contracts that compiles to the Ethereum bytecode. Scilla's semantic model is restricted, assuming actor based communication and restricting recursion, thus facilitating static analysis of Scilla contracts, and ensuring termination. Scilla is able to demonstrate that a number of popular Ethereum contracts avoid type errors, out-of-gas resource failures, and preservation of virtual currency. Scilla's semantics are defined formally, but have not yet been represented in a mechanised model.
Finally, the recent VerX tool is able to verify a range of specifications for solidity contracts automatically [52]. Similar to Chainmail, VerX has a specification language based on temporal logic. VerX offers three temporal operators (always, once, prev) but only within a past modality, while Chainmail has two temporal operators, both existential, but with both past and future modalities. VerX specifications can also include predicates that model the current invocation on a contract (similar to Chainmail's "calls"), can access variables, and compute sums (only) over collections. Chainmail is strictly more expressive as a specification language, including quantification over objects and sets (so can compute arbitrary reductions on collections) and of course specifications for permission ("access"), space ("in") and viewpoint ("external") which have no analogues in VerX. Unlike Chainmail, VerX includes a practical tool that has been used to verify a hundred properties across case studies of twelve Solidity contracts.

Conclusions
In this paper we have motivated the need for holistic specifications, presented the specification language Chainmail for writing such specifications, and outlined the formal foundations of the language. To focus on the key attributes of a holistic specification language, we have kept Chainmail simple, only requiring an understanding of first order logic. We believe that the holistic features (permission, control, time, space and viewpoint), are intuitive concepts when reasoning informally, and were pleased to have been able to provide their formal semantics in what we argue is a simple manner. L oo programs consist of modules, which are repositories of code. Since we study class based oo languages, in this work, code is represented as classes, and modules are mappings from identifiers to class descriptions.
Definition 11 (Modules). We define Module as the set of mappings from identifiers to class descriptions (the latter defined in Definition 12): Classes, as defined below, consist of field, method definitions and ghost field declarations. L oo is untyped, and therefore fields are declared without types, method signatures and ghost field signatures consist of sequences of parameters without types, and no return type. Method bodies consist of sequences of statements; these can be field read or field assignments, object creation, method calls, and return statements. All else, e.g. booleans, conditionals, loops, can be encoded. Field read or write is only allowed if the object whose field is being read belongs to the same class as the current method. This is enforced by the operational semantics, c.f. Fig. 4. Ghost fields are defined as implicit, side-effect-free functions with zero or more parameters. They are ghost information, i.e. they are not directly stored in the objects, and are not read/written during execution. When such a ghostfield is mentioned in an assertion, the corresponding function is evaluated. More in section B.2. Note that the expressions that make up the bodies of ghostfield declarations (e) are more complex than the terms that appear in individual statements.
From now on we expect that the set of field and the set of ghostfields defined in a class are disjoint.

A.2 The Operational Semantics of L oo
We will now define execution of L oo code. We start by defining the runtime entities, and runtime configurations, σ, which consist of heaps and stacks of frames. The frames are pairs consisting of a continuation, and a mapping from identifiers to values. The continuation represents the code to be executed next, and the mapping gives meaning to the formal and local parameters.
-We take addresses to be an enumerable set, Addr, and use the identifier α ∈ Addr to indicate an address. -Values, v, are either addresses, or sets of addresses or null: v ∈ {null} ∪ Addr ∪ P(Addr). -Continuations are either statements (as defined in Definition 12) or a marker, x:= •, for a nested call followed by statements to be executed once the call returns. Continuation ::= Stmts | x:= • ; Stmts -Frames, φ, consist of a code stub and a mapping from identifiers to values: φ ∈ CodeStub × Ident → V alue, -Stacks, ψ, are sequences of frames, ψ ::= φ | φ · ψ.
-Objects consist of a class identifier, and a partial mapping from field identifier to values: Object = ClassID × (FieldId → V alue). -Heaps, χ, are mappings from addresses to objects: χ ∈ Addr → Object.
Note that values may be sets of addresses. Such values are never part of the execution of L oo , but are used to give semantics to assertions . Next, we define the interpretation of variables (x) and field look up (x.f) in the context of frames, heaps and runtime configurations; these interpretations are used to define the operational semantics and also the validity of assertions, later on in Definitions 3-7.
Definition 15 (Interpretations). We first define lookup of fields and classes, where α is an address, and f is a field identifier: We now define interpretations as follows: fldMap) and fldMap(f)=v For ease of notation, we also use the shorthands below: In the definition of the operational semantics of L oo we use the following notations for lookup and updates of runtime entities : Definition 16 (Lookup and update of runtime configurations). We define convenient shorthands for looking up in runtime entities.
-Assuming that φ is the tuple (stub, varM ap), we use the notation φ.contn to obtain stub. Execution of a statement has the form M, σ ; σ , and is defined in figure 4.

Definition 17 (Execution)
. of one or more steps is defined as follows: -The relation M, σ ; σ , it is defined in Figure 4.

A.3 Module linking
When studying validity of assertions in the open world we are concerned with whether the module under consideration makes a certain guarantee when executed in conjunction with other modules. To answer this, we need the concept of linking other modules to the module under consideration. Linking, • , is an operation that takes two modules, and creates a module which corresponds to the union of the two. We place some conditions for module linking to be defined: We require that the two modules do not contain implementations for the same class identifiers,

A.4 Module pairs and visible states semantics
A module M adheres to an invariant assertion A, if it satisfies A in all runtime configurations that can be reached through execution of the code of M when linked to that of any other module M , and which are external to M. We call external to M those configurations which are currently executing code which does not come from M. This allows the code in M to break the invariant internally and temporarily, provided that the invariant is observed across the states visible to the external client M .
We have defined two module execution in the main paper, Def. 1.
In that definition n is allowed to have the value 2. In this case the final bullet is trivial and there exists a direct, external transition from σ to σ . Our definition is related to the concept of visible states semantics, but differs in that visible states semantics select the configurations at which an invariant is expected to hold, while we select the states which are considered for executions which are expected to satisfy an invariant. Our assertions can talk about several states (through the use of the will _ and was _ connectives), and thus, the intention of ignoring some intermediate configurations can only be achieved if we refine the concept of execution.
We have defined initial and arising configurations in Definition 2. Note that there are infinitely many different initial configurations, they will be differing in the code stored in the continuation of the unique frame.

B Foundations: Specification Language
We now define the syntax and semantics of expressions and holistic assertions. The novel, holistic, features of Chainmail (permission, control, time, space, and viewpoint), as well as our wish to support some form of recursion while keeping the logic of assertions classical, introduced challenges, which we discuss in this section.

B.1 Syntax of Assertions
Definition 19 (Assertions). Assertions consist of (pure) expressions e, classical assertions about the contents of heap/stack, the usual logical connectives, as well as our holistic concepts.
Expressions support calls with parameters (e.f(e * )); these are calls to ghostfield functions. This supports recursion at the level of expressions; therefore, the value of an expression may be undefined (either because of infinite recursion, or because the expression accessed undefined fields or variables). Assertions of the form e=e are satisfied only if both e and e are defined. Because we do not support recursion at the level of assertions, assertions from a classical logic (e.g. A ∨ ¬A is a tautology).
We will discuss evaluation of expressions in section B.2, standard assertions about heap/stack and logical connectives in B.3. We have discussed the treatment of permission, control, space, and viewpoint in the main text in the Definitions 3-7 in section 5.1 the treatment of time in Definitions 8,9 in the main text, section 5.2, We will discuss properties of assertions in Lemmas 2-3. The judgement M M , σ |= A expresses that A holds in M M and σ, and while M M , σ |= A expresses that A does not hold in M M and σ.

B.2 Values of Expressions
The value of an expression is described through judgment M, σ, e → v, defined in Figure 5. We use the configuration, σ, to read the contents of the top stack frame (rule Var_Val) or the contents of the heap (rule Field_Heap_Val). We use the module, M, to find the ghost field declaration corresponding to the ghost field being used.
The treatment of fields and ghost fields is described in rules Field_Heap_Val, Field_Ghost_Val and Field_Ghost_Val2. If the field f exists in the heap, then its value is returned (Field_Heap_Val). Ghost field reads, on the other hand, have the form e 0 .f(e 1 , ...e n ), and their value is described in rule Field_Ghost_Val: The lookup function G (defined in the obvious way in the Appendix, Def.13) returns the expression constituting the body for that ghost field, as defined in the class of e 0 . We return that expression evaluated in a configuration where the formal parameters have been substituted by the values of the actual parameters.
Ghost fields support recursive definitions. For example, imagine a module M 0 with a class Node which has a field called next, and which had a ghost field last, which finds the last Node in a sequence and is defined recursively as if this.next=null then this else this.next.last, and another ghost field acyclic, which expresses that a sequence is acyclic, defined recursively as if this.next=null then true else this.next.acyclic.
The relation → is partial. For example, assume a configuration σ 0 where acyc points to a Node whose field next has value null, and cyc points to a Node whose field next has the same value as cyc. Then, M 0 , σ 0 , acyc.acyclic → true, but we would have no value for M 0 , σ 0 , cyc.last → ..., nor for M 0 , σ 0 , cyc.acyclic → ....
Notice also that for an expression of the form e.f, both Field_Heap_Val and Field_Ghost_Val2 could be applicable: rule Field_Heap_Val will be applied if f is a field of the object at e, while rule Field_Ghost_Val will be applied if f is a ghost field of the object at e. We expect the set of fields and ghost fields in a given class to be disjoint. This allows a specification to be agnostic over whether a field is a physical field or just ghost information. For example, assertions (1) and (2) from section 2 talk about the balance of an Account. In module M BA1 (Appendix C), where we keep the balances in the account objects, this is a physical field. In M BA2 (also in Appendix C), where we keep the balances in a ledger, this is ghost information.

B.3 Satisfaction of Assertions -standard
We now define the semantics of assertions involving expressions, the heap/stack, and logical connectives. The semantics are unsurprising, except, perhaps the relation between validity of assertions and the values of expressions.

Definition 20 (Interpretations for simple expressions).
For a runtime configuration, σ, variables x or S, we define its interpretation as follows: Definition 21 ( Basic Assertions). For modules M, M , configuration σ, we define: We now define satisfaction of assertions which involve logical connectives and existential or universal quantifiers, in the standard way: for some α ∈ dom(σ), and z free in σ and A.
We define equivalence of assertions in the usual sense: two assertions are equivalent if they are satisfied in the context of the same configurations. Similarly, an assertion entails another assertion, iff all configurations which satisfy the former also satisfy the latter.   In this section we revisit the Bank/Account example from 2, and show two different implementations, derived from Noble et al. [49] . Both implementations satisfy the three functional specifications and the holistic assertions (1), (2) and (3) shown in section 2. The first version gives rise to runtime configurations as σ 1 , shown on the left side of Fig. 2, while the second version gives rise to runtime configurations as σ 2 , shown on the right side of Fig. 2. in the main text.
In this code, we use more syntax than the minimal syntax defined for L oo in Def. 11, as we use conditionals, and we allow nesting of expressions, e.g. a field read to be the receiver of a method call. Such extension can easily be encoded in the base syntax.
M BA1 , the fist version is shown Fig. 6. It keeps all the information in the Account object: namely, the Account contains the pointer to the bank, and the balance, while the Bank is a pure capability, which contains no state but is necessary for the creation of new Accounts. In this version we have no ghost fields.
M BA1 , the second version is shown Fig. 9 and 7. It keeps all the information in the ledger: each Node points to an Account and contains the balance for this particular  Account. Here balance is a ghost field of Account; the body of that declaration calls the ghost field function balanceOf of the Bank which in its turn calls the ghost field function balanceOf of the Node. Note that the latter is recursively defined.
Note also that Node exposes the function addToBalance(...); a call to this function modifies the balance of an Account without requiring that the caller has access to the Account. This might look as if it contradicted assertions (1) and (2) from section 2. However, upon closer inspection, we see that the assertion is satisfied. Remember that we employ a two-module semantics, where any change in the balance of an account is observed from one external state, to another external state. By definition, a configuration is external if its receiver is external. However, no external object will ever have access to a Node, and therefore no external object will ever be able to call the method addToBalance(...). In fact, we can add another assertion, (4), which promises that any internal object which is externally accessible is either a Bank or an Account.  Figure 9. MBA2: Implementation of Account -version 2 D Examplar: Authorising ERC20 ERC20 [61] is a widely used token standard which describes the basic functionality expected by any Ethereum-based token contract. It issues and keeps track of participants' tokens, and supports the transfer of tokens between participants. Transfer of tokens can take place only provided that there were sufficient tokens in the owner's account, and that the transfer was instigated by the owner, or by somebody authorized by the owner.
We specify this in Chainmail as follows: A decrease in a participant's balance can only be caused by a transfer instigated by the account holder themselves (i.e. p calls ....transfer( ...) ), or by an authorized transfer instigated by another participant p (i.e. p calls ...transferFrom( ..) ) who has authority for more than the tokens spent (i.e. e.allowed(p, p ) ≥ m) ∀e : ERC20.∀p : Object.∀m, m : Nat.
[ p calls e.transfer( p , m) ∨ e.allowed(p, p ) ≥ m ∧ p calls e.transferFrom( p , m) ] ] That is to say: if next configuration witnesses a decrease of p's balance by m, then the current configuration was a call of transfer instigated by p, or a call of transferFrom instigated by somebody authorized by p. The term e.allowed(p, p ), means that the ERC20 variable e holds a field called allowed which maps pairs of participants to numbers; such mappings are supported in Solidity [17].
We now define what it means for p to be authorized to spend up to m tokens on p's behalf: At some point in the past, p gave authority to p to spend m plus the sum of tokens spent so far by p on the behalf of p.
[ e.allowed(p, p ) = m −→ Prev p calls e.approve( p , m) ∨ e.allowed(p, p ) = m ∧ ¬( p calls e.transferFrom( p, _) ∨ p calls e.approve( p, _) ) ∨ ∃p : Object.∃m' : Nat. In more detail p is allowed to spend up to m tokens on their behalf of p, if in the previous step either a) p made the call approve on e with arguments p and m, or b) p was allowed to spend up to m tokens for p and did not transfer any of p's tokens, nor did p issue a fresh authorization, or c) p was authorized for m + m and spent m .
Thus, the holistic specification gives to account holders an "authorization-guarantee": their balance cannot decrease unless they themselves, or somebody they had authorized, instigates a transfer of tokens. Moreover, authorization is not transitive: only the account holder can authorise some other party to transfer funds from their account: authorisation to spend from an account does not confer the ability to authorise yet more others to spend also.
With traditional specifications, to obtain the "authorization-guarantee", one would need to inspect the pre-and post-conditions of all the functions in the contract, and determine which of the functions decrease balances, and which of the functions affect authorizations. In the case of the ERC20, one would have to inspect all eight such specifications (given in appendix D.1), where only five are relevant to the question at hand. In the general case, e.g. the DAO, the number of functions which are unrelated to the question at hand can be very large.
More importantly, with traditional specifications, nothing stops the next release of the contract to add, e.g. , a method which allows participants to share their authority, and thus violate the "authorization-guarantee", or even a super-user from skimming 0.1% from each of the accounts.

D.1 Example -ERC20, the traditional specification
We compare the holistic and the traditional specification of ERC20 As we said earlier, the holistic specification gives to account holders an "authorizationguarantee": their balance cannot decrease unless they themselves, or somebody they had authorized, instigates a transfer of tokens. Moreover, authorization is not transitive: only the account holder can authorise some other party to transfer funds from their account: authorisation to spend from an account does not confer the ability to authorise yet more others to spend also.
With traditional specifications, to obtain the "authorization-guarantee", one would need to inspect the pre-and post-conditions of all the functions in the contract, and determine which of the functions decrease balances, and which of the functions affect authorizations. In Figure 10 we outline a traditional specification for the ERC20. We give two speficiations for transfer, another two for tranferFrom, and one for all the remaining functions. The first specification says, e.g. , that if p has sufficient tokens, and it calls transfer, then the transfer will take place. The second specification says that if p has insufficient tokens, then the transfer will not take place (we assume that in this specification language, any entities not mentioned in the pre-or post-condition are not affected).
Similarly, we would have to give another two specifications to define the behaviour of if p" is authorized and executes transferFrom, then the balance decreases. But they are implicit about the overall behaviour and the necessary conditions, e.g., what are all the possible actions that can cause a decrease of balance?  The DAO (Decentralised Autonomous Organisation) [13] is a famous Ethereum contract which aims to support collective management of funds, and to place power directly in the hands of the owners of the DAO rather than delegate it to directors. Unfortunately, the DAO was not robust: a re-entrancy bug exploited in June 2016 led to a loss of $50M, and a hard-fork in the chain [16]. With holistic specifications we can write a succinct requirement that a DAO contract should always be able to repay any owner's money. Any contract which satisfies such a holistic specification cannot demonstrate the DAO bug.
Our specification consists of three requirements. First, that the DAO always holds at least as much money as any owner's balance. To express this we use the field balances which is a mapping from participants's addresses to numbers. Such mapping-valued fields exist in Solidity, but they could also be taken to be ghost fields [12].
∀d : DAO.∀p : Any.∀m : Nat. More cases are needed to reflect the financing and repayments of proposals, but they can be expressed with the concepts described so far. The requirement that d holds at least m ether precludes the DAO bug, in the sense that any contract satisfying that spec cannot exhibit the bug: a contract which satisfies the spec is guaranteed to always have enough money to satisfy all repay requests. This guarantee holds, regardless of how many functions there are in the DAO. In contrast, to preclude the DAO bug with a classical spec, one would need to write a spec for each of the DAO functions (currently 19), a spec for each function of the auxiliary contracts used by the DAO, and then study their emergent behaviour.
These 19 DAO functions have several different concerns: who may vote for a proposal, who is eligible to submit a proposal, how long the consultation period is for deliberating a proposal, what is the quorum, how to chose curators, what is the value of a token, Of these groups of functions, only a handful affect the balance of a participant. Holistic specifications allow us to concentrate on aspect of DAO's behaviour across all its functions.

F Examplar: Attenuating the DOM
Attenuation is the ability to provide to third party objects restricted access to an object's functionality. This is usually achieved through the introduction of an intermediate object. While such intermediate objects are a common programming practice, the term was coined, and the practice was studied in detail in the object capabilities literature, e.g. [40].
The key structure underlying a web browser is the Domain Object Model (DOM), a recursive composite tree structure of objects that represent everything display in a browser window. Each window has a single DOM tree which includes both the page's main content and also third party content such as advertisements. To ensure third party content cannot affect a page's main content, specifications for attenuation for the DOM were proposed in Devriese et al: [21].
This example deals with a tree of DOM nodes: Access to a DOM node gives access to all its parent and children nodes, and the ability to modify the node's properties. However, as the top nodes of the tree usually contain privileged information, while the lower nodes contain less crucial third-party information, we want to be able to limit access given to third parties to only the lower part of the DOM tree. We do this through a Wrapper, which has a field node pointing to a Node, and a field height which restricts the range of Nodes which may be modified through the use of the particular Wrapper. Namely, when you hold a Wrapper you can modify the property of all the descendants of the height-th ancestors of the node of that particular Wtrapper.
In Figure 11 we show an example of the use of Wrapper objects attenuating the use of Nodes The function usingWrappers takes as parameter an object of unknown provenance, here called unknwn. On lines 2-7 we create a tree consisting of nodes n1, n2, ... n6, depicted as blue circles on the right-hand-side of the Figure. On line 8 we create a wrapper of n5 with height 1. This means that the wrapper w may be used to modify n3, n5 and n6 (i.e. the objects in the green triangle), while it cannot be used to modify n1, n2, and 4 (i.e. the objects within the blue triangle). On line 8 we call a function named untrusted on the unknown object, and pass w as argument.
Even though we know nothing about the unknown object or its untrusted function, and even though the call gives to unknown access to w, which in turn has transitive access to all Node-s in the tree, we know that line 100 will not affect the property fields of the nodes n1, n2, and n4. Thus, the assertion on line 12 is guaranteed to succeed. The question is how do we specify Wrapper, so as to be able to make such an argument.
A specification of the class Wrapper in the traditional style, e.g.
[31] consists of pairs of pre-and post-conditions for each of the functions of that class. Each such pair gives a sufficient condition for some effect to take place: for example the call w.setProperty(i,prp) where i is smaller than w.height is a sufficient condition to modify property of the i-th parent of w.node. But we do not know what other ways there may be to modify a node's property. In other words, we have not specified the necessary conditions. In our example: method usingWrappers(unknwn) { n1=Node(null,"fixed"); n2=Node(n1,"robust"); n3=Node(n2,"const"); n4=Node(n3,"volatile"); n5=Node(n4,"variable"); n6=Node(n5,"ethereal"); w=Wrapper(n5,1); unknwn.untrusted(w); assert n2.property==" robust" ... } n5 n1 n6 n2 n4 n3 unknwn w Figure 11. Wrappers protecting Nodes The necessary condition for the modification of nd.property for some nd of class Node is either access to some Node in the same tree, or access to a w of class Wrapper where the w.height-th parent of w is an ancestor of nd.
With such a specification we can prove that the assertion on line 12 will succeed. And, more importantly, we can ensure that all future updates of the Wrapper abstract data type will uphold the protection of the Node data. To give a flavour of Chainmail, we use it express the requirement from above: ∀S : Set.∀nd : Node.∀o : Object. That is, if the value of nd.property is modified (changes _ ) at some future point (will _ ) and if reaching that future point involves no more objects than those from set S (i.e. _ in S ), then at least one (o) of the objects in S is not a Node nor a Wrapper, and o has direct access to some node ( o access nd ), or to some wrapper w and the w.height-th parent of w is an ancestor of nd (that is, parnt k = w.node.parnt w.height ). Note that our "access" is intransitive: x access y holds if either x has a field pointing to y, or x is the receiver and y is one of the arguments in the executing method call.