1 Introduction

Smart contracts [21] are computer programs that transfer digital assets between users without a trusted authority. Currently, smart contracts are supported by several blockchains, the first and most widespread one being Ethereum [9]. Users interact with a smart contract by sending transactions, which trigger state updates, and may possibly involve transfers of crypto-assets between the called contract and the users. The sequence of transactions on the blockchain determines the state of each contract, and the balance of each user.

The blockchain is maintained by a peer-to-peer network of nodes, which follow a consensus protocol to determine, at each turn, a new block of transactions to be added to the blockchain. This protocol guarantees the correct execution of contracts also in the presence of (a minority of) adversaries in the network, and ensures that all the nodes have the same view of their state. Nodes play the role of miner or that of validator. Miners gather from the network sets of transactions sent by users, and execute them serially to determine the new state. Once a block is appended to the blockchain, validators re-execute all its transactions, to update their local view of the contracts state and of the users’ balance. To do this, validators process the transactions exactly in the same order in which they occur in the block, since choosing a different order could potentially result in inconsistencies between the nodes (note that miners also act as validators, since they validate all the blocks received from the network).

Although executing transactions in a purely sequential fashion is quite effective to ensure the consistency of the blockchain state, in the age of multi-core processors it fails to properly exploit the computational capabilities of nodes. By enabling miners and validators to concurrently execute transactions, it would be possible to improve the efficiency and the throughput of the blockchain.

This paper exploits techniques from concurrency theory to provide a formal backbone for parallel executions of transactions. More specifically, our main contributions can be summarised as follows:

  • As a first step, we formalise blockchains, giving their semantics as a function which maps each contract to its state, and each user to her balance. This semantics reflects the standard implementation of nodes, where transactions are evaluated in sequence, without any concurrency.

  • We introduce two notions of swappability of transactions. The first is purely semantic: two adjacent transactions can be swapped if doing so preserves the semantics of the blockchain. The second notion, called strong swappability, is more syntactical: it checks a simple condition (inspired by Bernstein’s conditions [7]) on static approximations of the variables read/written by the transactions. Theorem 2 shows that strong swappability is strictly included in the semantic relation. Further, if we transform a blockchain by repeatedly exchanging adjacent strongly swappable transactions, the resulting blockchain is observationally equivalent to the original one (Theorem 4).

  • Building upon strong swappability, we devise a true concurrent model of transactions execution. To this purpose, we transform a block of transactions into an occurrence net, describing exactly the partial order induced by the swappability relation. We model the concurrent executions of a blockchain in terms of the step firing sequences (i.e. finite sequences of sets of transitions) of the associated occurrence net. Theorem 5 establishes that the concurrent executions and the serial one are semantically equivalent.

  • We describe how miners and validators can use our results to concurrently execute transactions, exploiting the multi-core architecture available on their nodes. Remarkably, our technique is compatible with the current implementation of the Ethereum blockchain, while the other existing approaches to parallelize transactions execution would require a soft-fork.

  • We apply our technique to ERC-721 tokens, one of the most common kinds of contracts in Ethereum, showing them to be suitable for parallelization.

Because of space constraints, all the proofs of our results are in [6].

2 Transactions and Blockchains

In this section we introduce a general model of transactions and blockchains, abstracting from the actual smart contracts language.

A smart contract is a finite set of functions, i.e. terms of the form , where is a function name, \(\varvec{x_{}}\) is the sequence of formal parameters (omitted when empty), and is the function body. We postulate that the functions in a contract have distinct names. We abstract from the actual syntax of , and we just assume that the semantics of function bodies is defined (see e.g. [5] for a concrete instance of syntax and semantics of function bodies).

Let be a set of values, ranged over by , let be a set of constant names \(x_{}, y_{}, \ldots \), and let be a set of addresses , partitioned into account addresses and contract addresses . We assume a mapping from addresses to contracts.

We assume that each contract has a key-value store, which we render as a partial function from keys to values. The state of the blockchain is a function from addresses to key-value stores. We postulate that for all . A qualified key is a term of the form . We write for ; when , we write . We use \({p}_{},{q}_{},\ldots \) to range over qualified keys, \(P_{},Q_{},\ldots \) to range over sets of them, and \(\mathbb {P}\) to denote the set of all qualified keys.

To have a uniform treatment of accounts and contracts, we assume that for all account addresses , , and that the contract has exactly one function, which just skips. In this way, the statement , which transfers n currency units to , can be rendered as a call to this function.

State updates define how values associated with qualified keys are modified.

Definition 1 (State update)

A state update is a function from qualified keys to values; we denote with the state update which maps to . We define \({\text {keys}}({\pi _{}})\) as the set of qualified keys such that and . We apply updates to states as follows:

We denote with the semantics of the statement . This semantics is either a blockchain state \(\sigma _{}'\), or it is undefined (denoted by \(\bot \)). The semantics is parameterised over a state \(\sigma _{}\), an address (the contract wherein is evaluated), and an environment , used to evaluate the formal parameters and the special names and . These names represent, respectively, the caller of the function, and the amount of currency transferred along with the call. We postulate that and are not used as formal parameters.

We define the auxiliary operators \(+\) and − on states as follows:

i.e., updates \(\sigma _{}\) by increasing the of of currency units.

A transaction is a term of the form:

Intuitively, is the address of the caller, is the address of the called contract, is the called function, n is the value transferred from to , and is the sequence of actual parameters. We denote the semantics of in \(\sigma _{}\) as , where the function is defined in Fig. 1, which we briefly comment.

Fig. 1.
figure 1

Semantics of transactions.

The semantics of a transaction , in a given blockchain state \(\sigma _{}\), is a new state \(\sigma _{}'\). Rule [Tx1] handles the case where the transaction is successful: this happens when ’s balance is at least , and the function call terminates in a non-error state. Note that units of currency are transferred to before starting to execute , and that the names and are bound, respectively, to and . Rule [Tx2] applies either when ’s balance is not enough, or the execution of fails. In these cases, does not alter the state.

A blockchain is a finite sequence of transactions; we denote with the empty blockchain. The semantics of a blockchain is obtained by folding the semantics of its transactions, starting from a given state \(\sigma \):

Note that erroneous transactions can occur within a blockchain, but they have no effect on its semantics (as rule [Tx2] makes them identities w.r.t. the append operation). We assume that in the initial state of the blockchain, denoted by \(\sigma ^{\star }\), each address has a balance , while all the other keys are unbound.

We write for , where . We say that a state \(\sigma _{}\) is reachable if for some .

Example 1

Consider the following functions of a contract at address :

Let \(\sigma _{}\) be a state such that , and let , where:

By applying rule [Tx1] three times, we have that:

Summing up, . \(\diamond \)

3 Swapping Transactions

We define two blockchain states to be observationally equivalent when they agree on the values associated to all the qualified keys. Our formalisation is parameterised on a set of qualified keys \(P_{}\) over which we require the agreement.

Definition 2 (Observational equivalence)

For all \({P_{}\subseteq \mathbb {P}}\), we define \(\sigma _{}\sim _{P_{}} \sigma _{}'\) iff \(\forall {p}_{}\in P_{}: \sigma _{}{p}_{}= \sigma _{}'{p}_{}\). We say that \(\sigma _{}\) and \(\sigma _{}'\) are observationally equivalent, in symbols \(\sigma _{}\sim \sigma _{}'\), when \(\sigma _{}\sim _{P_{}} \sigma _{}'\) holds for all \(P_{}\). \(\diamond \)

Lemma 1

For all \(P_{},Q_{}\subseteq \mathbb {P}\): (i) \(\sim _{P_{}}\) is an equivalence relation; (ii) if \(\sigma _{}\sim _{P_{}} \sigma _{}'\) and \(Q_{}\subseteq P_{}\), then \(\sigma _{}\sim _{Q_{}} \sigma _{}'\); (iii) \(\sim \, = \, \sim _{\mathbb {P}}\). \(\diamond \)

We extend the equivalence relations above to blockchains, by passing through their semantics. For all \(P_{}\), we define iff holds for all reachable \(\sigma _{}\) (note that all the definitions and results in this paper apply to reachable states, since the unreachable ones do not represent actual contract executions). We write holds for all \(P_{}\). The relation \(\sim \) is a congruence with respect to the append operation, i.e. if then we can replace with in a larger blockchain, preserving its semantics.

Lemma 2

. \(\diamond \)

Two transactions are swappable when exchanging their order preserves observational equivalence.

Definition 3 (Swappability)

Two transactions are swappable, in symbols , when . \(\diamond \)

Example 2

Recall the transactions in Example 1. We have that and , but (see Fig. 5 in Appendix A of [6]). \(\diamond \)

We shall use the theory of trace languages originated from Mazurkiewicz’s works [17] to study observational equivalence under various swapping relations. Below, we fix the alphabet of trace languages as the set of all transactions.

Definition 4 (Mazurkiewicz equivalence)

Let I be a symmetric and irreflexive relation on . The Mazurkiewicz equivalence \(\simeq _{I}\) is the least congruence in the free monoid such that: :

Theorem 1 below states that the Mazurkiewicz equivalence constructed on the swappability relation \(\rightleftarrows \) is an observational equivalence. Therefore, we can transform a blockchain into an observationally equivalent one by a finite number of exchanges of adjacent swappable transactions.

Theorem 1

\(\simeq _{\rightleftarrows } \;\; \subseteq \;\; \sim \). \(\diamond \)

Example 3

We can rearrange the transactions in Example 1 as . Instead, (e.g., starting from a state \(\sigma _{}\) such that and , see Fig. 6 in Appendix A of [6]). \(\diamond \)

Note that the converse of Theorem 1 does not hold: indeed, requires that and have the same length, while may also hold for blockchains of different length (e.g., , where does not alter the state).

Safe Approximations of Read/Written Keys. Note that the relation \(\rightleftarrows \) is undecidable whenever the contract language is Turing-equivalent. So, to detect swappable transactions we follow a static approach, consisting of two steps. First, we over-approximate the set of keys read and written by transactions, by statically analysing the code of the called functions. We then check a simple condition on these approximations (Definition 7), to detect if two transactions can be swapped. Since static analyses to over-approximate read and written variables are quite standard [18], here we just rely on such approximations, by only assuming their safety. In Definition 5 we state that a set \(P_{}\) safely approximates the keys written by , when does not alter the state of the keys not in \(P_{}\). Defining set of read keys is a bit trickier: intuitively, we require that if we execute the transaction starting from two states that agree on the values of the keys in the read set, then these executions should be equivalent, in the sense that they do not introduce new differences between the resulting states (with respect to the difference already existing before).

Definition 5 (Safe approximation of read/written keys)

Given a set of qualified keys \(P_{}\) and a transaction , we define:

Example 4

Let , where is a function of . The execution of affects the of , and ; however, is first incremented and then decremented, and so its value remains unchanged. Then, , and it is the smallest safe approximation of the keys written by . To prove that , assume two blockchains and and a set of keys Q such that and . If , then by [Tx2] we have . Since , then also , and so by [Tx2] we have . Then, . Otherwise, if , then by [Tx1] the execution of transfers one unit of currency from to , so the execution of affects exactly and . So, it is enough to show that implies for . For , we have that . For , we have that . Therefore, we conclude that . \(\diamond \)

Widening a safe approximation (either of read or written keys) preserves its safety; further, the intersection of two safe write approximations is still safe (see Lemma 6 in Appendix A of [6]). From this, it follows that there exists a least safe approximation of the keys written by a transaction.

Strong Swappability. We use safe approximations of the read/written keys to detect when two transactions are swappable. To achieve that, we check whether two transactions and operate on disjoint portions of the blockchain state. More specifically, we recast in our setting Bernstein’s conditions [7] for the parallel execution of processes: it suffices to check that the set of keys written by is disjoint from those written or read by , and vice versa. When this happens we say that the two transactions are strongly swappable.

Definition 6 (Strong swappability)

We say that two transactions are strongly swappable, in symbols , when there exist \(W,W',R,R' \subseteq \mathbb {P}\) such that , , , , and:

$$ \big ( R \cup W \big ) \cap W' \; = \; \emptyset \; = \; \big ( R' \cup W' \big ) \cap W \qquad \qquad \qquad \qquad {\diamond } $$

Example 5

Let and be functions of the contracts and , respectively, and consider the following transactions:

where , , and are account addresses. To prove that , consider the following safe approximations of the written/read keys of and , respectively:

Since \((W_1 \cup R_1) \cap W_2 = \emptyset = (W_2 \cup R_2) \cap W_1\), the two transactions are strongly swappable. Now, let:

and consider the following safe approximations \(W_3\) and \(R_3\):

Since \(W_1 \cap W_3 \ne \emptyset \ne W_2 \cap W_3\), then and . \(\diamond \)

The following theorem ensures the soundness of our approximation, i.e. that if two transactions are strongly swappable, then they are also swappable. The converse implication does not hold, as witnessed by Example 6.

Theorem 2

. \(\diamond \)

Example 6 (Swappable transactions, not strongly)

Consider the following functions and transactions of a contract at address :

We prove that . First, consider a state \(\sigma _{}\) such that , , , and . We have that:

In the second case, let \(\sigma _{}\) be such that , or , or , or . It is not possible that the guards in and are both true, so or raise an exception, leaving the state unaffected. Then, also in this case we have that , and so and are swappable. However, they are not strongly swappable if there exist reachable states such that . To see why, let . From the code of we see that \(W_1\) is the least safe over-approximation of the written keys of (). This means that every safe approximation of must include the keys of \(W_1\). Similarly, is the least safe over-approximation of the written keys of (). Since the least safe approximations of the keys written by and are not disjoint, does not hold. \(\diamond \)

Theorem 3 states that the Mazurkiewicz equivalence \(\simeq _{\#^{}_{}}\) is stricter than \(\simeq _{\rightleftarrows }\). Together with Theorem 1, if is transformed into by exchanging adjacent strongly swappable transactions, then and are observationally equivalent.

Theorem 3

\(\simeq _{\#^{}_{}} \; \subseteq \; \simeq _{\rightleftarrows }\). \(\diamond \)

Note that if the contract language is Turing-equivalent, then finding approximations which satisfy the disjointness condition in Definition 6 is not computable, and so the relation \(\#^{}_{}\) is undecidable.

Parameterised Strong Swappability. Strongly swappability abstracts from the actual static analysis used to obtain the safe approximations: it is sufficient that such an analysis exists. Definition 7 below parameterises strong swappability over a static analysis, which we represent as a function from transactions to sets of qualified keys, just requiring it to be a safe approximation. Formally, we say that is a static analysis of written keys when , for all ; similarly, is a static analysis of read keys when , for all .

Definition 7 (Parameterised strong swappability)

Let and be static analyses of written/read keys. We say that , are strongly swappable w.r.t. and , in symbols , if:

Note that an effective procedure for computing and gives an effective procedure to determine whether two transactions are (strongly) swappable.

Lemma 3

For all static analyses and : (i) ; (ii) if and are computable, then \(\#^{W}_{R}\) is decidable. \(\diamond \)

From the inclusion in item (i) of Lemma 3 and from Theorem 3 we obtain:

Theorem 4

\(\simeq _{\#^{W}_{R}} \; \subseteq \; \simeq _{\#^{}_{}} \; \subseteq \; \simeq _{\rightleftarrows }\). \(\diamond \)

4 True Concurrency for Blockchains

Given a swappability relation , we transform a sequence of transactions into an occurrence net , which describes the partial order induced by . Any concurrent execution of the transactions in which respects this partial order is equivalent to the serial execution of (Theorem 5).

From Blockchains to Occurrence Nets. We start by recapping the notion of Petri net [19]. A Petri net is a tuple \(\mathsf {N}= (\mathsf{P}, \mathsf{Tr}, \mathsf{{F}}, \mathsf{{m}_{0}})\), where \(\mathsf{P}\) is a set of places, \(\mathsf{Tr}\) is a set of transitions (with \(\mathsf{P}\cap \mathsf{Tr}= \emptyset \)), and \(\mathsf{{F}}: (\mathsf{P}\times \mathsf{Tr}) \cup (\mathsf{Tr}\times \mathsf{P}) \rightarrow \mathbb {N}\) is a weight function. The state of a net is a marking, i.e. a multiset \(\mathsf{{m}_{}}: \mathsf{P}\rightarrow \mathbb {N}\) defining how many tokens are contained in each place; we denote with \(\mathsf{{m}_{0}}\) the initial marking. The behaviour of a Petri net is specified as a transition relation between markings: intuitively, a transition \(\mathsf {t}_{}\) is enabled at \(\mathsf{{m}_{}}\) when each place \(\mathsf{{p_{}}}\) has at least \(\mathsf{{F}}(\mathsf{{p_{}}},\mathsf {t}_{})\) tokens in \(\mathsf{{m}_{}}\). When an enabled transition \(\mathsf {t}_{}\) is fired, it consumes \(\mathsf{{F}}(\mathsf{{p_{}}},\mathsf {t}_{})\) tokens from each \(\mathsf{{p_{}}}\), and produces \(\mathsf{{F}}(\mathsf {t}_{},\mathsf{{p'_{}}})\) tokens in each \(\mathsf{{p'_{}}}\). Formally, given \(x \in \mathsf{P}\cup \mathsf{Tr}\), we define the preset \({}^{\bullet }{x}\) and the postset \({x}{{}^{\bullet }}\) as multisets: \({}^{\bullet }{x}(y) = \mathsf{{F}}(y,x)\), and \({x}{{}^{\bullet }}(y) = \mathsf{{F}}(x,y)\). A transition \(\mathsf {t}_{}\) is enabled at \(\mathsf{{m}_{}}\) when \({}^{\bullet }{\mathsf {t}_{}} \subseteq \mathsf{{m}_{}}\). The transition relation between markings is defined as \(\mathsf{{m}_{}}\xrightarrow {\mathsf {t}_{}} \mathsf{{m}_{}}- {}^{\bullet }{\mathsf {t}_{}} + {\mathsf {t}_{}}{{}^{\bullet }}\), where \(\mathsf {t}_{}\) is enabled. We say that is a firing sequence from \(\mathsf{{m}_{}}\) to \(\mathsf{{m}'_{}}\) when , and in this case we say that \(\mathsf{{m}'_{}}\) is reachable from \(\mathsf{{m}_{}}\). We say that \(\mathsf{{m}'_{}}\) is reachable when it is reachable from \(\mathsf{{m}_{0}}\).

An occurrence net [8] is a Petri net such that: (i) \(|{\mathsf{{p_{}}}}{{}^{\bullet }}| \le 1\) for all \(\mathsf{{p_{}}}\); (ii) \(|{}^{\bullet }{\mathsf{{p_{}}}}| = 1\) if \(\mathsf{{p_{}}}\not \in \mathsf{{m}_{0}}\), and \(|{}^{\bullet }{\mathsf{{p_{}}}}| = 0\) if \(\mathsf{{p_{}}}\in \mathsf{{m}_{0}}\); (iii) \(\mathsf{{F}}\) is a relation, i.e. \(\mathsf{{F}}(x,y) \le 1\) for all xy; (iv) \(\mathsf{{F}}^*\) is a acyclic, i.e. \(\forall x,y \in \mathsf{P}\cup \mathsf{Tr}: (x,y) \in \mathsf{{F}}^* \wedge (y,x) \in \mathsf{{F}}^* \implies x=y\) (where \(\mathsf{{F}}^*\) is the reflexive and transitive closure of \(\mathsf{{F}}\)).

In Fig. 2 we transform a blockchain into a Petri net , where is an arbitrary relation between transactions. Although any relation ensures that is an occurrence net (Lemma 4 below), our main results hold when is a strong swappability relation. The transformation works as follows: the i-th transaction in is rendered as a transition , and transactions related by are transformed into concurrent transitions. Technically, this concurrency is specified as a relation < between transitions, such that whenever \(i < j\), but and are not related by . The places, the weight function, and the initial marking of are chosen to ensure that the firing ot transitions respects the relation <.

Fig. 2.
figure 2

Construction of a Petri net from a blockchain .

Example 7

Consider the following transactions and functions of a contract :

Let It is easy to check that these sets are safe approximations of their transactions (e.g., safely approximates the keys written by ). By Definition 6 we have that , , but . We display in Fig. 3, where , , and . Note that can only be fired after , while can be fired independently from and . This is coherent with the fact that is swappable with both and , while and are not swappable. \(\diamond \)

Fig. 3.
figure 3

Occurrence net for Example 7.

Lemma 4

is an occurrence net, for all and .

Step Firing Sequences. Theorem 5 below establishes a correspondence between concurrent and serial execution of transactions. Since the semantics of serial executions is given in terms of blockchain states \(\sigma _{}\), to formalise this correspondence we use the same semantics domain also for concurrent executions. This is obtained in two steps. First, we define concurrent executions of as the step firing sequences (i.e. finite sequences of sets of transitions) of . Then, we give a semantics to step firing sequences, in terms of blockchain states.

We denote finite sets of transitions, called steps, as \(\mathsf{U}_{},\mathsf{U'}_{},\ldots \). Their preset and postset are defined as \( \textstyle {}^{\bullet }{\mathsf{U}_{}} = \sum _{\mathsf{{p_{}}}\in \mathsf{U}_{}} {}^{\bullet }{\mathsf{{p_{}}}} \) and \( \textstyle {\mathsf{U}_{}}{{}^{\bullet }} = \sum _{\mathsf{{p_{}}}\in \mathsf{U}_{}} {\mathsf{{p_{}}}}{{}^{\bullet }} \), respectively. We say that \(\mathsf{U}_{}\) is enabled at \(\mathsf{{m}_{}}\) when \({}^{\bullet }{\mathsf{U}_{}} \le \mathsf{{m}_{}}\), and in this case firing \(\mathsf{U}_{}\) results in the move \(\mathsf{{m}_{}}\xrightarrow {\mathsf{U}_{}} \mathsf{{m}_{}}- {}^{\bullet }{\mathsf{U}_{}} + {\mathsf{U}_{}}{{}^{\bullet }}\). Let be a finite sequence of steps. We say that \(\varvec{\mathsf{U}_{}}\) is a step firing sequence from \(\mathsf{{m}_{}}\) to \(\mathsf{{m}'_{}}\) if , and in this case we write \(\mathsf{{m}_{}}\xrightarrow {\varvec{\mathsf{U}_{}}} \mathsf{{m}'_{}}\).

Concurrent Execution of Transactions. We now define how to execute transactions in parallel. The idea is to execute transactions in isolation, and then merge their changes, whenever they are mutually disjoint. The state updated resulting from the execution of a transaction are formalised as in Definition 1.

An update collector is a function that, given a state and a transaction , gives an update which maps (at least) the updated qualified keys to their new values. In practice, update collectors can be obtained by instrumenting the run-time environment of smart contracts, so to record the state changes resulting from the execution of transactions. We formalise update collectors abstracting from the implementation details of such an instrumentation:

Definition 8 (Update collector)

We say that a function is an update collector when , for all and . \(\diamond \)

There exists a natural ordering of collectors, which extends the ordering between state updates (i.e., set inclusion, when interpreting them as sets of substitutions): namely, holds when . The following lemma characterizes the least update collector w.r.t. this ordering.

Lemma 5 (Least update collector)

Let , where we define as . Then, is the least update collector. \(\diamond \)

The merge of two state updates is the union of the corresponding substitutions; to avoid collisions, we make the merge operator undefined when the domains of the two updates overlap.

Definition 9 (Merge of state updates)

Let \(\pi _{0}\), \(\pi _{1}\) be state updates. When \({\text {keys}}({\pi _{0}}) \cap {\text {keys}}({\pi _{1}}) = \emptyset \), we define \(\pi _{0} \oplus \pi _{1}\) as follows:

The merge operator enjoys the commutative monoidal laws, and can therefore be extended to (finite) sets of state updates.

We now associate step firing sequences with state updates. The semantics of a step in \(\sigma _{}\) is obtained by applying to \(\sigma _{}\) the merge of the updates , for all \(i \in 1..n\)—whenever the merge is defined. The semantics of a step firing sequence is then obtained by folding that of its steps.

Definition 10 (Semantics of step firing sequences)

We define the semantics of step firing sequences, given and , as:

Example 8

Let , and be as in Example 7, and let . Since , , and , we have:

Note that, for all \(\sigma _{}\):

So, the serial execution of and (in both orders) is equal to their concurrent execution (similarly for and ). Instead, for all such that :

So, concurrent executions of and may differ from serial ones. This is coherent with the fact that, in Fig. 3, and are not concurrent. \(\diamond \)

Concurrent Execution of Blockchains. Theorem 5 relates serial executions of transactions to concurrent ones (which are rendered as step firing sequences). Item (a) establishes a confluence property: if two step firing sequences lead to the same marking, then they also lead to the same blockchain state. Item (b) ensures that the blockchain, interpreted as a sequence of transitions, is a step firing sequence, and it is maximal (i.e., there is a bijection between the transactions in the blockchain and the transitions of the corresponding net). Finally, item (c) ensures that executing maximal step firing sequences is equivalent to executing serially the blockchain.

Theorem 5

Let . Then, in :

  1. (a)

    if \(\mathsf{{m}_{0}} \xrightarrow {\varvec{\mathsf{U}_{}}} \mathsf{{m}_{}}\) and \(\mathsf{{m}_{0}} \xrightarrow {\varvec{\mathsf{U'}_{}}} \mathsf{{m}_{}}\), then , for all reachable ;

  2. (b)

    is a maximal step firing sequence;

  3. (c)

    for all maximal step firing sequences , for all reachable , .

Remarkably, the implications of Theorem 5 also apply to .

Example 9

Recall and from Example 7, let , and let be such that . As predicted by item (c) of Theorem 5:

Let . We have that and lead to the same marking, where the places , and contain one token each, while the other places have no tokens. By item (a) of Theorem 5 we conclude that . Now, let . Note that, although \(\varvec{\mathsf{U''}_{}}\) is maximal, it is not a step firing sequence, since the second step is not enabled (actually, and are not concurrent, as pointed out in Example 8). Therefore, the items of Theorem 5 do not apply to \(\varvec{\mathsf{U''}_{}}\), coherently with the fact that \(\varvec{\mathsf{U''}_{}}\) does not represent any sequential execution of . \(\diamond \)

5 Case Study: ERC-721 Token

We now apply our theory to an archetypal Ethereum smart contract, which implements a “non-fungible token” following the standard ERC-721 interface [14, 15]. This contract defines the functions to transfer tokens between users, and to delegate their trade to other users. Currently, token transfers involve \(\sim \)50% of the transactions on the Ethereum blockchain [1], with larger peaks due to popular contracts like Cryptokitties [22].

We sketch below the implementation of the Token contract, using Solidity, the main high-level smart contract language in Ethereum (see Appendix B of [6] for the full implementation).

The contract state is defined by the following mappings:

figure c

Each token is uniquely identified by an integer value (of type uint256), while users are identified by an address. The mapping owner maps tokens to their owners’ addresses (the zero address is used to denote a dummy owner). The mapping exists tells whether a token has been created or not, while balance gives the number of tokens owned by each user. The mapping operatorApprovals allows a user to delegate the transfer of all her tokens to third parties.

The function transferFrom transfers a token from the owner to another user. The require assertion rules out some undesirable cases, e.g., if the token does not exist, or it is not owned by the from user, or the user attempts to transfer the token to himself. Once all these checks are passed, the transfer succeeds if the sender of the transaction owns the token, or if he has been delegated by the owner. The mappings owner and balance are updated as expected.

figure d

The function setApprovalForAll delegates the transfers of all the tokens of the sender to the operator when the boolean isApproved is true, otherwise it revokes the delegation.

figure e

Assume that user owns two tokens, identified by the integers 1 and 2, and consider the following transactions:

We have that , , and (this can be proved e.g. by using the static approximations in Appendix B of [6]), while the other combinations are not swappable. Let . The resulting occurrence net is displayed in Fig. 4. For instance, let , i.e. and are executed concurrently, as well as and . From item (c) of Theorem 5 we have that this concurrent execution is equivalent to the serial one.

Although this example deals with the marginal case where the sender and the receiver of tokens overlap, in practice the large majority of transactions in a block either involves distinct users, or invokes distinct ERC-721 interfaces, making it possible to increase the degree of concurrency of transferFrom transactions.

Fig. 4.
figure 4

Occurrence net for the blockchain of the ERC-721 token.

6 Related Work and Conclusions

We have proposed a static approach to improve the performance of blockchains by concurrently executing transactions. We have started by introducing a model of transactions and blockchains. We have defined two transactions to be swappable when inverting their order does not affect the blockchain state. We have then introduced a static approximation of swappability, based on a static analysis of the sets of keys read/written by transactions. We have rendered concurrent executions of a sequence of transactions as step firing sequences in the associated occurrence net. Our main technical result, Theorem 5, shows that these concurrent executions are semantically equivalent to the sequential one.

We can exploit our results in practice to improve the performances of miners and validators. Miners should perform the following steps to mine a block:

  1. 1.

    gather from the network a set of transactions, and put them in an arbitrary linear order , which is the mined block;

  2. 2.

    compute the relation on , using a static analysis of read/written keys;

  3. 3.

    construct the occurrence net ;

  4. 4.

    execute transactions concurrently according to the occurrence net, exploiting the available parallelism.

The behaviour of validators is almost identical to that of miners, except that in step (1), rather than choosing the order of transactions, they should adhere to the ordering of the mined block . Note that in the last step, validators can execute any maximal step firing sequence which is coherent with their degree of parallelism: item (c) of Theorem 5 ensures that the resulting state is equal to the state obtained by the miner. The experiments in [12] suggest that parallelization may lead to a significant improvement of the performance of nodes: the benchmarks on a selection of representative contracts show an overall speedup of 1.33\(\times \) for miners and 1.69\(\times \) for validators, using only three cores.

Note that malevolent users could attempt a denial-of-service attack by publishing contracts which are hard to statically analyse, and therefore are not suitable for parallelization. This kind of attacks can be mitigated by adopting a mining strategy that gives higher priority to parallelizable transactions.

Applying Our Approach to Ethereum. Applying our theory to Ethereum would require a static analysis of read/written keys at the level of EVM bytecode. As far as we know, the only tool implementing such an analysis is ES-ETH [16]. However, the current version of the tool has several limitations, like e.g. the compile-time approximation of dictionary keys and of values shorter than 32 bytes, which make ES-ETH not directly usable to the purposes of our work. In general, precise static analyses at the level of the Ethereum bytecode are difficult to achieve, since the language has features like dynamic dispatching and pointer aliasing which are notoriously a source of imprecision for static analysis. However, coarser approximations of read/written keys may be enough to speed-up the execution of transactions. For instance, in Ethereum, blocks typically contain many transactions which transfer tokens between participants, and many of them involve distinct senders and receivers. A relatively simple analysis of the code of token contracts (which is usually similar to that in Sect. 5) may be enough to detect that these transactions are swappable.

Aiming at minimality, our model does not include the gas mechanism, which is used in Ethereum to pay miners for executing contracts. The sender of a transaction deposits into it some crypto-currency, to be paid to the miner which appends the transaction to the blockchain. Each instruction executed by the miner consumes part of this deposit; when the deposit reaches zero, the miner stops executing the transaction. At this point, all the effects of the transaction (except the payment to the miner) are rolled back. Our transaction model could be easily extended with a gas mechanism, by associating a cost to statements and recording the gas consumption in the environment. Remarkably, adding gas does not invalidate approximations of read/written keys which are correct while neglecting gas. However, a gas-aware analysis may be more precise of a gas-oblivious one: for instance, in the statement (where is a function which exceeds the available gas) a gas-aware analysis would be able to detect that x is not written.

Related Work. A few works study how to optimize the execution of smart contracts on Ethereum, using dynamic techniques adopted from software transactional memory [4, 12, 13]. These works are focussed on empirical aspects (e.g., measuring the speedup obtained on a given benchmark), while we focus on the theoretical counterpart. In [12, 13], miners execute a set of transactions speculatively in parallel, using abstract locks and inverse logs to dynamically discover conflicts and to recover from inconsistent states. The obtained execution is guaranteed to be equivalent to a serial execution of the same set of transactions. The work [4] proposes a conceptually similar technique, but based on optimistic software transactional memory. Since speculative execution is non-deterministic, in both approaches miners need to communicate the chosen schedule of transactions to validators, to allow them to correctly validate the block. This schedule must be embedded in the mined block: since Ethereum does not support this kind of block metadata, these approaches would require a “soft-fork” of the blockchain to be implemented in practice. Instead, our approach is compatible with the current Ethereum, since miners only need to append transactions to the blockchain. Compared to [4, 12], where conflicts are detected dynamically, our approach relies on a static analysis to detect potential conflicts. Since software transactional memory introduces a run-time overhead, in principle a static technique could allow for faster executions, at the price of a preprocessing phase. Saraph and Herlihy [20] study the effectiveness of speculatively executing smart contracts in Ethereum. They sample past blocks of transactions (from July 2016 to December 2017), replay them by using a speculative execution engine, and measure the speedup obtained by parallel execution. Their results show that simple speculative strategies yield non-trivial speed-ups. Further, they note that many of the data conflicts (i.e. concurrent read/write accesses to the same state location) arise in periods of high traffic, and they are caused by a small number of popular contracts, like e.g. tokens.

In the permissioned setting, Hyperledger Fabric [3] follows the “execute first and then order” paradigm: transactions are executed speculatively, and then their ordering is checked for correctness [2]. In this paradigm, appending a transaction requires a few steps. First, a client proposes a transaction to a set of “endorsing” peers, which simulate the transaction without updating the blockchain. The output of the simulation includes the state updates of the transaction execution, and the sets of read/written keys. These sets are then signed by the endorsing peers, and returned to the client, which submits them to the “ordering” peers. These nodes order transactions in blocks, and send them to the “committing” peers, which validate them. A block is valid when, if a key is read by transaction , then has not been written by a transaction with \(j<i\). Finally, validated blocks are appended to the blockchain. Our model is coherent with Ethereum, which does not support speculative execution.

Future Works. A relevant line of research is the design of domain-specific languages for smart contracts that are directly amenable to techniques that, like ours, increase the degree of concurrency of executions. For this purpose, the language should support static analyses of read/written keys, like the one we use to define the strong swappability relation. Although the literature describes various static analyses of smart contracts, most of them are focussed on finding security vulnerabilities, rather than enhancing concurrency.

Outside the realm of smart contracts, a few papers propose static analyses of read/written variables. The paper [11] describes an analysis based on separation logic, and applies it to resolve conflicts in the setting of snapshot isolation for transactional memory in Java. When a conflict is detected, the read/write sets are used to determine how the code can be modified to resolve it. The paper [10] presents a static analysis to infer read and write locations in a C-like language with atomic sections. The analysis is used to translate atomic sections into standard lock operations. The design of new smart contract languages could take advantage of these analyses.