Whitepaper V3
Whitepaper V3
Abstract
FastSet is a distributed protocol for decentralized finance and settle-
ment, which is inspired from both actors and blockchains. Account hold-
ers cooperate by making claims, which can include payments, holding and
transferring assets, accessing and updating shared data, medical records,
digital identity, and mathematical theorems, among others. The claims
are signed by their owners and are broadcast to a decentralized network
of validators, which validate and settle them. Validators replicate the
global state of the accounts and need not communicate with each other.
In sharp contrast to blockchains, strong consistency is purposely given up
as a requirement. Yet, many if not most of the blockchain benefits are
preserved, while capitalizing on actor’s massive parallelism. The protocol
is proved to be correct, despite its massively parallel nature.
Contents
1 Introduction 2
1
4.5 Settlement of Data and Records . . . . . . . . . . . . . . . . . . . 30
4.6 Settling Programming Languages and VMs . . . . . . . . . . . . 30
4.7 Settling Programs . . . . . . . . . . . . . . . . . . . . . . . . . . 31
4.8 Verifiable Computing . . . . . . . . . . . . . . . . . . . . . . . . . 33
4.9 Voting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
4.10 Games . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
4.11 Escrow . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
4.12 Non-Native Tokens and Digital Assets . . . . . . . . . . . . . . . 42
4.13 Sequencing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
4.14 Time Stamping . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
4.15 Auctions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
4.16 AppChains and Blockchains . . . . . . . . . . . . . . . . . . . . . 49
4.17 Beyond Weak Independence . . . . . . . . . . . . . . . . . . . . . 51
5 Conclusion 53
A Understanding SETL 58
A.1 Two Types of Accounts . . . . . . . . . . . . . . . . . . . . . . . 58
A.2 Contracts and Instances . . . . . . . . . . . . . . . . . . . . . . . 59
A.3 Templates and Parameters . . . . . . . . . . . . . . . . . . . . . . 60
A.4 Claim Block Assumptions and Non-Assumptions . . . . . . . . . 61
1 Introduction
Blockchains are strongly consistent distributed systems in which account holders
cooperate by making transactions. Transactions are intents of actions, which
need to be given a state to execute in order to materialize. Transactions are
signed by their owners and broadcast to a decentralized network of nodes, some-
times called miners or validators. The nodes play dice in order to identify a
proposer for the next block (or sequence) of transactions. Nodes replicate the
global state and follow a consensus protocol by which the proposed block is pro-
cessed by all the nodes, thus materializing all the transactions in the block and
each node updating its state accordingly. In the end, the transactions submitted
to the blockchain are totally ordered, and thus so are the states in between.
Strongly consistent distributed systems [12, 10, 14, 17] “behave like one com-
puter”, which is the desirable property for blockchains, but are notorious for
higher latency and lower performance and scalability—they require coordina-
tion and communication between nodes to maintain consistency. At the other
extreme stands the actor model [11, 1], aiming at highly parallel computing and
massive concurrency. Actors are the basic building blocks of concurrent com-
putation and communicate only through messages. In response to a message it
receives, an actor can modify its own private state, create more actors together
with code governing their behavior, and send messages to other actors.
2
There is no doubt that blockchains play an important role in modern finance.
Also, blockchains have demonstrated, in our view irreversibly, that decentraliza-
tion is not only possible in the world of digital assets, but also very much desir-
able. Not only decentralization addresses the single point of failure, corruption,
and censorship vulnerabilities, but equally importantly, through blockchains, it
has lead to the Web3 philosophy and movement: users own their digital assets,
from money to diplomas and medical records to pictures and messages, and they
and only they can transfer them and decide who has access to what. However,
a major overhaul is needed in order to scale blockchains and achieve the level
of high performance and low cost required by recent applications. For exam-
ple, few blockchains can consistently perform more than 1,000 transactions per
second (TPS) and it takes seconds, sometimes minutes, to settle a transaction.
Metaphorically, because of the total order on transactions that they enforce by
their nature, blockchains require the entire universe to squeeze through a nar-
row pipe. Completely unrelated transactions are required to stay in line and
wait to be sequenced in some order that the blockchain must globally choose
when forming its next block. This is clearly not scalable, even if all transactions
are initiated by humans. The situation is in fact much worse, because AI and
AI agents doing transactions on humans’ behalf are already here to stay and
they require a payment system able to perform millions of TPS, most of which
micro-transactions whose cost is expected to be negligible, in the order of frac-
tions of a cent. Blockchains cannot do this. A massively parallel decentralized
infrastructure for payments in particular and computing in general is required.
A series of papers before 2008 culminating with Bitcoin [16], have incremen-
tally built a belief that a total order on transactions ought to be required in
any distributed/decentralized payment system in order to avoid the infamous
double spending attack: an account sending two concurrent transactions at-
tempting to spend the same coin with two different recipients. A total ordering
enforced on transaction settlement indeed solves the double spending problem,
but is it really necessary? Recent works starting around 2019 [8, 9, 3] propose
a radically different way to approach the problem, a truly concurrent approach
where payments can be generated and settled in different orders by different
nodes without breaking the semantics of payments. The key insight of these
works is that the order in which an account receives payments is irrelevant,
and so is the order in which different accounts send payments—provided each
account stays valid: no double spending and sufficient balance. That is, as far
as the nodes/validators are in agreement on the set of locally valid transactions
that took place in the system, consensus on a total order is unnecessary. We
believe that this apparently simple and innocent observation marks a crucial
breakthrough moment in decentralized finance. A moment where the tyranny
of sequentiality is abolished and the door is open to innovations that will lead
to the next generation of decentralized, yet truly concurrent, safe, and scalable
infrastructure for digital assets.
Inspired by this recent work on weaker variants of consensus in the context
of cryptocurrencies [8, 9, 3], as well as by the unbounded concurrent comput-
ing promise of the actor model [11, 1], in this paper we propose FastSet, a
3
general-purpose distributed computing protocol that generalizes the payment
system protocol FastPay [3]. FastSet performs nearly embarrassingly parallel
settlement of arbitrary verifiable claims, including verifiable computations in
arbitrary programming languages. Specifically, FastSet allows a set of clients
to settle verifiable claims consistently, using a set of validators. Clients broad-
cast their claims to all validators. Validators validate the received claims and
replicate the system state based on the order in which they receive the claims.
Importantly, similarly to FastPay but in sharp contrast to blockchains, the Fast-
Set validators do not need to communicate with each other, nor directly achieve
consensus on any values or orders or blocks or computations.
What makes the problem difficult is that claims can have side effects on
the validators’ states. However, if claims issued by different clients are weakly
independent, a notion that generalizes the property of commutativity on pay-
ments [8, 9, 3] discussed above to arbitrary state-effectful computations, then
the validators’ states will remain (strongly eventually) consistent in spite of the
different orders in which they receive and process the claims.
Like in blockchains, FastSet accounts have a state (e.g., a balance) and
are required to sign any claims they issue. We present the protocol in Sec-
tion 2, requiring only abstract notions of state, claim, and claim validity and
effect. How claims are generated, e.g., randomly by users or programmatically
by contracts, is irrelevant for the core protocol and its correctness discussed in
Section 3.4. However, to make it practical, implementations of FastSet have
to offer specific types of claims and specific ways to generate them. In Sec-
tion 4 we propose an actor-inspired language for generating claims, which we
call the FastSet Language and abbreviate setl1 , and pronounce “settle”. Some
accounts are controlled by users, others by setl programs (or scripts, or con-
tracts). The difference is that the user-controlled accounts are free to issue any
claims, including ones that create new accounts and interact with them, while
the contract-based accounts can only issue claims as prescribed by their setl
program/script/contract.
Like in the actor model, accounts are regarded as actors that communicate
only with the actors they know about, and can create new accounts/actors and
then communicate with them. Since validators maintain replicas of the global
system state, all the actors/accounts are also replicated on all validators. This
should not be regarded as a deviation from the underlying thesis of the actor
model, where each actor is meant to be a separate process interacting concur-
rently with the other actors, but rather as a high-availability high-resilience
decentralized implementation of an actor system. Moreover, the actor model
is particularly suitable for a language like setl for two additional reasons: (1)
each validator can itself be a high-performance concurrent system, which re-
ceives and processes potentially millions of claims per second, so setl must be
a high-performance concurrent programming language; and (2) since each val-
idator receives the claims in different orders, yet all of them (strongly eventually
1 Not to be confused with the SET Language https://en.wikipedia.org/wiki/SETL, in-
4
consistently) are expected to replicate the same state, setl concurrent programs
must be easy to reason about, in particular to prove their determinism.
The rest of the paper is organized as follows. Section 2 defines the FastSet
Protocol. Section 3 proves the correctness of the protocol. Section 4 proposes,
through examples that are commonly encountered in Web3, an informal design
of setl, a contract scripting language for FastSet. Section 5 concludes the
paper. This paper also has two appendix sections. Appendix A elaborates on
the basic concepts that help the reader to better understand the examples in
Section 4. Appendix B discusses some possible extensions of the examples.
We recommend the reader to first read Section 2, to understand how the
FastSet protocol works. Then readers can take one of the following two paths.
The theoretically inclined reader can dive into the mathematical formalization
and the proofs of correctness of FastSet in Section 3. The reader interested in
applications that can take advantage of FastSet’s massively parallel architecture
can skim Section 3 and skip directly to Section 4. If some examples are difficult
to understand, the reader can check out the explanations in Appendix A.
2.1 Participants
FastSet involves two types of participants:
Clients. These are account/address holders, who can make claims. They can
be users, apps (contracts, web2, games), L1s, L2s, micro-chains, AI agents,
service providers, execution engines/layers, provers (mathematical, ZK),
TEEs, oracles, VRFs, AI compute/inferencers, indexers, history query
providers, verifiers (execution, semantics and/or ZK proof based), fact
claimers (KYC providers, digital identity providers, academic or medical
records, etc.), token issuers (stablecoins, ERCs, NFTs, RWAs, etc.), etc.
Validators. These process claims made by the clients, while at the same time
maintaining consistency in the global knowledge about the overall system:
balances of all tokens and additional data/state of all clients, global state
of the entire protocol, like the set of all the claims that were settled, etc.
All participants possess a key pair consisting of a private signature key and the
corresponding public verification key. If msg is a message and p is a participant,
then ⟨msg⟩p denotes the message signed by p, whose authenticity can be publicly
checked. Like in blockchains, in FastSet the public key of a client a serves as
the public account number, or the address of a. Additional layers of privacy can
be added if desired, but that is out of the scope of this paper.
5
2.2 Claims and Weak Independence
We define FastSet parametrically, on top of two important abstractions, claims
and their weak independence, which we discuss next.
A claim is any statement that is independently verifiable. To help the ver-
ifiers, the claim provider may associate additional evidence to the claim, such
as a proof or a witness or even an authoritative signature; how claims provide
evidence and how verifiers verify a claim is orthogonal to FastSet and is not
our concern in this paper. Here are some examples of claims: “I am Joe Smith”
(a fact where the signer is important); “Pythagoras theorem” (a fact where the
signer is less important); “the price of gold is 100 USD” (an oracle); “the next
random number is 17” (a VRF); “I want to buy a ticket to this concert” (an in-
tent); “the result to your query is 42” (an AI service provided); “Python program
fibonacci on input 10 evaluates to 55” (verifiers may re-execute, or require a
math or ZK proof based on Python formal semantics); “my Angry Birds score
is 739” (requires 3rd party or ZK proof); “my next move in this chess game
with Alice is Nf3” (modifying a shared storage location sequentially); “I vote
YES for that petition” (modifying a shared storage location non-sequentially);
“I, Grigore, pay Alice 10 USD” (a payment, modifying two storage locations).
For simplicity and generality, we only assume a global state in FastSet, which
will be replicated in each validator. In practice, each client will have their own
reserved state space, but that stronger assumption is not needed to prove the
correctness of FastSet; all we need is that claims issued by different clients are
weakly independent, to be discussed shortly. What is important is that a claim
may or may not be valid in a given state (e.g., a payment is invalid when the
sender’s balance is not enough), and that the processing of a claim can and
usually does change the state (e.g., even an otherwise side-effect-free claim may
be counted, at a minimum). We write c ↓s whenever a claim c is valid in a
state s, and in that case JcKs denotes the state obtained after processing c in
s. We extend these notations to sequences of claims c1 c2 ... ck as expected:
c1 c2 ... ck ↓s means that each claim in the sequence is valid in the state obtained
after processing the previous ones, and Jc1 c2 ... ck Ks is the state obtained after
processing the entire sequence. The root of difficulty in FastSet, as well as in
concurrent and distributed systems in general, is the fact that claim sequences
issued by different interacting clients may arrive to validators interleaved in
different ways, which may make their states diverge inconsistently.
Drawing inspiration from concurrency theory (e.g., Mazurkiewicz traces [15]),
where two events are independent iff they can be processed in any order with
the same result, we say that two claims c and c′ are weakly independent, written
c ∥ c′ , iff once each of them is independently valid, they can be processed in any
order and the final result is the same: c ∥ c′ iff for any state s, if c ↓s and c′ ↓s
then c c′ ↓s , c′ c ↓s , and Jc c′ Ks = Jc′ cKs. Therefore, we weaken the classic notion
of independence by requiring it to hold only in those states in which both claims
are already valid. This assumption is critical for FastSet because it allows to
handle payments and asset transfers as particular claims, and thus generalize
FastPay [3]. Here are some examples of weakly independent claims:
6
• Totally (not weakly) independent claims whose validity has nothing to do
with each other, such as: facts which are independently verifiable/true;
state queries which are pure, that is, return results but have no side effects;
state accesses, including writes/updates, but of disjoint storage locations.
• Commutative operations on a shared location: writing the same value,
like in setting a one-way flag to "true"; adding numbers, positive only like
in voting or both positive and negative like in reputation systems (e.g.,
the karma system of Reddit); multiplying numbers, like in aggregating
signatures; adding elements to a set, like in signing petitions, bidding in an
auction, or submitting (an intent to make) a transaction; adding elements
to a canonical data-structure, like to a sorted list (which is re-sorted after
each addition).
• Updates of CRDTs, abbreviating Commutative [20, 18] or Conflict-free [21]
Replicated Data Types, which are data-types used in the context of dis-
tributed systems with the property that concurrent updates on them com-
mute. CRDTs were used mainly in concurrent text editing, but the concept
is general and includes many interesting examples, such as integer vectors,
sets, maps, and even graphs (with some reasonable restrictions).
• Payments and, more generally, asset transfers from different clients.
Payments are, in particular, a very important use case of FastSet. As criti-
cally observed by the authors of FastPay [3], payments have the key property of
commutativity on the recipient. Specifically, if Charlie receives payments from
both Alice and Bob, then Charlie will end up with the same balance/state no
matter in what order the two payments from Alice and Bob are received. And
so will Alice and Bob. It can be easily seen that any two payments made by dis-
tinct clients are weakly independent. But they are not necessarily independent
with the standard notion of independence [15]. For example, the claims “Alice
pays Bob 1 USD” and “Bob pays Charlie 1 USD” are (weakly independent but)
not independent: indeed, consider a state in which Alice has 1 USD and Bob has
0 USD. The same state also demonstrates that two payments made by the same
client are not necessarily weakly independent, e.g., “Alice pays Bob 1 USD” and
“Alice pays Charlie 1 USD”. The weak independence of payments by different
clients says that the overall state of the system will be the same independently
of the order in which they are processed, provided that the initial state of the
system allows for each payment to be independently made.
Non-examples of weak independence include claims acquiring the same mu-
tex, or arbitrary accesses of a shared location where at least one is a write. In
particular, and this might look surprising at first sight, an exact balance claim
is not weakly independent with payment claims to that account. For example,
Alice’s claim “I have 20 USD” is not weakly independent with payment claims
made by others to Alice. On the other hand, monotonic balance claims of the
form “I have at least 20 USD” are weakly independent with payments made by
others. A challenge for FastSet implementations is to determine what consti-
tutes claims on their network and how they want to enforce weak independence.
7
2.3 Assumptions
In its most basic form, FastSet’s clients and validators form a bipartite graph,
where each client is connected to each validator but the clients and, respectively,
the validators, are not connected to each other. Specifically, the only communi-
cation required by the basic protocol is for each client and each validator to be
able to send signed messages to each other. In practice, however, some clients
will likely communicate with each other outside of the protocol in order to or-
chestrate and optimize their individual communication with the validators. For
example, a user’s AI agent may require an approval from the user before buying
a ticket; instead of communicating exclusively through claims and waiting for
each of them to be settled by FastSet before proceeding to the next step, the user
and the agent can both send their claims in parallel and settle the transaction
faster and cheaper. Similarly, in practice validators will likely have mechanisms
to communicate with each other in order to synchronize their states faster than
waiting for clients to send them the missing certificates. However, such side
communication mechanisms are outside the scope of this paper.
At a high-level, FastSet works as follows. At any given moment, any client
can broadcast to validators an ordered block of claims in a signed message.
Each validator checks the validity of each block of claims received from each
client, and if everything checks the validator approves the block by signing the
client’s message and sending it back to the client without updating its state
yet. The client collects approvals from validators and once it reaches quorum it
broadcasts a confirmation message to all validators. Once a validator receives
the confirmation it proceeds to updating its state. Special care must be paid by
the validator to order the state updates to avoid inconsistencies. Eventually, all
validators will consistently settle all the valid claims made by all the clients.
The validators do not question, nor check the intent behind or the client-
specific semantics of the client’s claims. The validators only check the claims’
validity and settle them if valid, operations which are expected to be very effi-
cient; indeed, in practice these operations involve no computations other than
updating client balances and storage locations with given values. In other words,
from validators’ perspective the client claims are just simple commands that
they must comply with after a few sanity checks. Any computations using pro-
grams or smart contracts in various programming languages and VMs happen
within the clients, using their resources and not validators’. It is not valida-
tors’ concern whether client’s computations are adequate or correct according
to client’s intended semantics or specs or terms and conditions or whatever
promises they may have made to their clients. All the validators need to know
is the block of claims that the client issues as a result of those potentially com-
plex computations. As an analogy, the validators play the role of an operating
system (OS) in a computer, whose job is to ensure consistency across the various
programs being executed; the programs are the clients of the OS and all they
do from OS’s perspective is to issue ordered blocks of commands (the claims).
FastSet therefore gives clients ultimate freedom in what claims they can
issue. Without any additional verification, it is quite possible that some clients
8
may make mistakes, some of them even maliciously. For example, the client may
hold user assets, such as in the case of a bank, or a centralized exchange, or a
blockchain. That is, users may send their assets to the client in expectation of
some services. Without any additional verification, there is nothing to prevent
the client from stealing users’ assets. Even without any evil intent, a client’s
account may be operated by a complex program which may have subtle errors
(such as unintended non-determinism) and thus sometimes produce an incorrect
sequence of claims. Even without any program at all, a user client like Alice
may mistype 11 instead of 1 when sending a payment to Bob, in which case
additional verification, like a multi-sig, would have helped.
There is no silver-bullet solution to cover all types of claim verification that
clients or applications need or will ever need. To continue to give clients max-
imum flexibility, yet to allow the honest clients to verifiably prove themselves
to their users, FastSet provides the capability for clients to set up verifiers and
a quorum among those in order for its claims to be considered valid by the
FastSet validators. Verifiers are themselves clients on the network, whose role
is to provide specialized verification services. For example, a verifier can be
specialized in verifying program executions in EVM (or Python, or Java, etc.)
and clients, e.g., blockchains (or AI agents, or exchanges, etc.), can use it. Such
a verifier may re-execute the EVM program to check the result, or may verify
a proof produced by an external compute, such as a ZK proof or a TEE proof
or even just a signature by some trusted authority (e.g., a centralized exchange
which offers services to its KYC-ed clients). It is the client’s full responsibility
to decide what verifiers to include in their set and what quorum is sufficient.
Implementations of FastSet may optionally provide a simple hardwired pro-
gramming or scripting language to generate sequences of claims. Programs, or
smart contracts, or scripts in this language would be executed by the validators
on behalf of clients. That is, instead of providing a block of claims to each val-
idator, a client would provide a script that instructs validators how to generate
the block of claims on their behalf, possibly using a client-provided input, at
the cost of paying some gas fees to the validators. In this case, clients must pay
special care to ensure that each validator deterministically generates the same
claims in the same order. Section 4 presents a candidate scripting language.
To simplify the presentation of FastSet and to uniformly capture more use
cases, we assume the existence of a proxy that facilitates the interaction between
a client, its verifiers, and the validators. The proxy is just a helper which only
affects the liveness, but not the safety of the protocol. All it does is to forward or
broadcast messages signed by the various participants, and to blindly aggregate
signatures (#{sig1 , sig2 , ...}). Should the proxy fail to do its job for whatever
reason, anybody can replace it and redo the tasks that were missed, including
the client. Proxy can be a client (the sender of the original message containing
the claim sequence, or a different client such as a beneficiary or a verifier or
a service provider), or a validator, or any combination of these for all or for
any of the two services it provides, namely broadcasting client messages and
aggregating validator signatures. That is, there could be zero, one, or more
proxies for each client or application deployed on FastSet. If zero, then the
9
client initiating the claims can do the job of the proxy, or any of the validators.
An honest validator always follows the FastSet protocol, while a faulty (or
Byzantine) validator may deviate arbitrarily. Assume 3f + 1 validators, with a
fixed unknown subset of at most f Byzantine validators. A quorum is defined
as any subset of 2f + 1 validators. A protocol message signed by a quorum of
validators is certified and called a certificate.
2.4 FastSet
The validator’s goal is to consistently replicate the global state, while at the same
time offer services through an API. Each validator v is assumed to maintain the
following data at a minimum, and to offer it through its API at request:
• A state v.state, which is its version of the global state that is replicated in
each validator. In implementations of FastSet the state may be partitioned
in objects and storage locations owned by or associated to different clients,
as well as shared objects and locations, but this distinction is not necessary
in the presentation of the core protocol, because the weak independence
abstraction between claims by different clients suffices. Each validator
may have a different state at any given moment due to message delays
and to the massive parallelism allowed by FastSet. Yet, we will show that
the various state replicas are consistent and they eventually converge.
10
1 Claim Proxy Validators
Client
2 Verify
Verifiers
3 Validate
4 Sign
5 Confirm
... ...
... 6 Presettle
7 Settle
Proxy is a helper and only affects liveness, not safety. It can be a client (the sender
of the original message containing the claim sequence, or a different client such as
a beneficiary or a verifier or a service provider), a validator, or any combination
of these for all or for any of the two services it provides: broadcasting messages
to validators and signature aggregation (#{sig1 , sig2 , ...}).
7 Settle any message in v.presettle that can be settled: if m = ⟨c1 c2 ... ck , n⟩a
in v.presettled with v(a).nonce = n and c1 c2 ... ck ↓v.state then v.state :=
Jc1 c2 ... ck Kv.state, increment v(a).nonce, reset v(a).pending := ∅, move m to
v.settled. This step 7 can and should be executed continuously.
11
claim blocks with the same nonce, because that is the FastSet equivalent of a
double spending attempt. Regardless, the protocol will ensure that the client
will only be able to obtain validator quorum on at most one claim block per
nonce. A client submitting multiple claim blocks with the same nonce risks
to get stuck in a pending status with the validators and never achieve quorum
to make progress. We do not treat this situation here, but implementations of
FastSet may choose to allow clients to clear their pending status for a fee, to
give them an exit situation in case their programs have errors but at the same
time to discourage them from attempting to break or slow down the protocol.
2 Validators will not attempt to validate any received claim block before
it accumulates an appropriate number of verifiers which approve it. This infor-
mation being public, verifiers and clients have multiple ways to synchronize to
get this task done. The simplest, but also the least efficient and riskiest for the
client, is for the client to just submit its message and assume that its verifiers
listen to some validator and will thus get informed when the client message was
settled, then verify it, sign it and send it to the proxy which then aggregates
all their signatures and sends them to the validators. A more efficient approach
is for the client to inform its verifiers as soon as it sends the original message,
so they can start their verification process immediately. An even more efficient
approach in some applications could be for the client to send its verifiers its
intent to execute a program, also known to the verifiers, so that they can start
their verification while the client is still working on generating its claim block,
allowing program execution and its verification to take place in parallel.
3 Once the validator v has been informed that a client’s claim block
c1 c2 ... ck accumulated the quorum of verifiers, it can proceed to validating it.
The obvious and cheap checks are the signatures, the nonce, and the verifier
quorum. We have deliberately left open the decision on where the verifiers and
the verifier quorum are stored: some implementations of FastSet may store them
in the validator, e.g., v(a).verifiers and v(a).quorum, in which case the check
becomes |T ∩ v(a).verifiers| ≥ v(a).quorum; others may store them as additional
items in the message, besides the claim block and nonce; others may include a
special claim in the block, say at the end, claiming the verifiers and quorum.
In all cases, however, the verifiers and their quorum are part of the contract
that the application makes with its users, so it is important that the valida-
tors enforce them. The validator also checks that there is no different message
sent by the same client with the same nonce that is pending, that is, it checks
v(a).pending ⊆ {m}. In other words, if there is any message pending then it
must be the exact same message m. This ensures that the client does not, inten-
tionally or not, attempt to initiate double-spending attacks or non-deterministic
computations. Note that messages from validators can be delayed or lost, so the
client or its proxy may attempt to submit m multiple times until they receive
validator confirmation, which is why v(a).pending is allowed to already contain
m. Finally, validator v is ready to check the validity of the actual claim block
c1 c2 ... ck in its state v.state, that is, c1 c2 ... ck ↓v.state . Note that the validator
does not update its state at this stage. Indeed, the block has not achieved val-
12
idator (not verifier) quorum yet, which means that the client may still possibly
achieve quorum on a different block with other validators.
4 Validator v now approves the claim block by signing the original client’s
message and sending it back. The validator also sets v(a).pending to {m}, so
from here on it will accept no other messages from a until m is settled. If the
validator received the same message m from a that it approved already, it sends
its approval again to account for previous messages having been potentially lost.
5 When the proxy accumulates validator quorum on message m, it creates
a certificate ⟨m⟩# by aggregating all the received validator confirmations and
broadcasts it back to all validators. This certificate is proof that quorum has
been achieved and at this moment the state updates in all the validators are
imminent, i.e., cannot be stopped anymore once they receive the certificate.
6 When a validator v receives such a valid certificate ⟨m⟩# , it knows with
certainty that a quorum of validators have already validated m, so it is safe to
process and settle m. But that needs to be done only once, to avoid applying
the same updates twice or more, so v first checks that m is not already settled,
or considered to be settled, i.e., presettled. If that is the case, then v should
settle m. However, v may not be ready to settle m, in the sense that the nonce
or the claim block of m may not be valid for v. In fact, there is no guarantee
that v even participated in the quorum of ⟨m⟩# , or that v is even aware of the
existence of the client that originally sent m, because that particular client and
v could have been disconnected by now. Taking into account all these situations,
v only adds m to v.presettle for now.
7 The liveness result, Theorem 4 (see also 4 in Theorem 1), tells that if
other validators settled more claims than v, then v should also be able to make
progress and settle more claims. That is, if v continuously scans v.presettle it
will eventually find messages which are valid and thus can be settled, because
FastSet assumes that all messages are eventually delivered. Implementations
of FastSet will likely include mechanisms for validators to request updates of
missing settled claims from other validators and populate their presettled set
with them in order to make faster progress. Note that right before m is settled
in this step 7 of the protocol, v(a).pending can be either empty or {m}. The
former happens when v did not get a chance to participate in the quorum on
m, so v never checked c1 c2 ... ck ↓v.state (e.g., if a had enough balance to make
a payment); however, since v(a).nonce = n, v is ready to settle m as soon as
c1 c2 ... ck ↓v.state , and it does exactly that. The latter happens when v has al-
ready signed m and was waiting for a quorum including other validators as well
to be formed. The monotonicity result, Theorem 3 (see also 3 in Theorem 1),
tells that it is safe to settle m as is, that is, checking c1 c2 ... ck ↓v.state is the-
oretically unnecessary. However, we choose to keep this definedness check. As
seen in Section 4, implementations of FastSet may choose to allow applications
to disobey the weak independence requirement. Keeping the definedness check
will guarantee that the validator’s state stays well-defined, the damage being
contained to possibly locking the non-conforming accounts.
Theorem 1. Under the assumptions in Section 2.3, FastSet is correct, i.e.:
13
1. Security All certificates generated at Step 5 are consistent, that is, only
one per client per nonce. Therefore, FastSet prevents double-spending.
2. Determinism The order in which a validator receives the messages is
irrelevant.
3. Monotonicity Once a client can settle a message, it will continue to be
able to settle it regardless of what other clients do.
4. Liveness Validators do not get stuck: if one makes progress, then all do.
Proof. We only prove the security property here and the Byzantine aspect of
monotonicity and liveness. The other properties are deferred to Section 3.4,
because they require a more formal setting.
Recall that we assumed 3f + 1 validators, with at most f Byzantine. Recall
also that a quorum is any subset of 2f + 1 validators, so there are sufficient
honest validators to fill the quorum for any valid message. That is, even if the
Byzantine validators decide to stay silent and not send their signed messages in
Step 4, a quorum will still be reached for any valid message. Therefore, when
proving the monotonicity and liveness properties in Section 3.4, we can assume
that the validator advanced to Step 6.
The Byzantine validators can behave arbitrarily maliciously, though, and
they can do so in combination with arbitrarily many cooperating clients which
they can create and control. Note, however, that no invalid message can ever be
signed (in Step 4) by any honest validator. Since any message quorum includes
at least one honest validator and since signature aggregation cannot be forged
in Step 5, we conclude that only valid messages are received at Step 6, and
thus only valid messages can ever be settled by honest validators in Step 7.
Therefore, as far as each client obeys the protocol, that is, issues one sequence
of claims, all honest validators will eventually observe the exact same sequence.
But what if a client, or its proxy, conspires with the Byzantine validators and
attempts to fork its sequence of claims, that is, to send two different messages
with the same nonce? We show that this is not possible. Indeed, each of the
two different messages with the same nonce need to reach quorum of 2f + 1
validators. Let’s assume there are b ≤ f Byzantine validators, that is, 3f + 1 − b
honest validators. Then each quorum has at least 2f + 1 − b honest validators.
Suppose by contradiction that the respective sets of honest validators in the two
quorums are disjoint. Then it means that there are at least 2(2f + 1 − b) =
4f +2−2b ≥ 3f +2−b honest validators, which is a contradiction. Consequently,
the two quorums must have at least one honest validator in common. But then
this honest validator would never sign two different messages with the same
nonce, because of the v(a).pending ⊆ {m} clause in Step 3.
14
consistency in distributed systems. These concepts do not apply directly to our
setting but are closely related, and they inspired our formalization and results.
Strong consistency [12] states that every data item update is propagated
to all nodes before any other access (each read returns the latest write value,
regardless of the node on which the read is executed). Strong consistency is often
used synonymously with linearizability [10], which means that the distributed
system behaves like one computer. This is the commonly used notion of protocol
correctness in blockchains, which are metaphorically called “world computers” to
convey their linearizability property, and is achieved using consensus algorithms
like Paxos [13, 14] and Raft [17] among many recent ones. Strong consistency
guarantees both safety and liveness, and it is the most comforting property
that a distributed system can have. Unfortunately, in practice it comes at the
expense of performance and scalability, and, in the context of blockchains, it
also results in high fees per transaction. In fact, the single-owner object in
[8, 9] and then FastPay [3] were proposed as alternatives to strongly consistent
protocols precisely for these reasons, demonstrating that strong consistency can
be significantly weakened for many applications, including payment systems
like Bitcoin [16]. FastSet falls into the same category; i.e., it is not strongly
consistent.
Eventual consistency [22], on the other hand, states that data item updates
will propagate through the system and eventually be applied to all nodes, given
enough time (each read in each node eventually returns the latest write). Put
differently, if no new updates are made to a particular data item, eventually,
all nodes will converge on the same value for that item. Eventual consistency
is therefore a liveness property, and it is considered the minimal notion of con-
sistency that still has practical value. However, eventual consistency tends to
be too weak for many applications, especially interactive and reactive systems
meant to execute forever, like our FastSet, because it does not say anything
about the safety, or consistency, of the intermediate values of the data items.
Strong eventual consistency (SEC) [21] brings safety to eventual consistency,
in the following sense: any two nodes that have received the same (possibly in-
terleaved) updates will be in the same state. SEC is clearly the desired property
for FastSet as well. In FastSet terminology, it says that if two different valida-
tors process the same claim sequences for the same clients, then they end up in
the same state regardless of how the claim sequences were interleaved. That is,
from the collective perspective of the clients, validators behave deterministically
and will not diverge. Or put differently, they are in “consensus”.
However, the concurrency model underlying FastSet differs from classic (replica-
based) distributed systems models like those in [21]. In distributed systems
models, a number of communicating nodes perform a computation together.
Accesses to shared objects impose certain constraints on the distributed com-
putation, such as all the reads seeing the latest write value, but are also oppor-
tunities for nodes to share causality information. In FastSet, validators do not
initiate any computation nor exchange any information with each other, nei-
ther directly nor indirectly through clients, and there is no causal dependence
shared through updates (nor defined or calculated). Clients drive the protocol
15
by broadcasting claims that may update the validator’s states. The only means
of communication in FastSet, if it can even be called so, is that a client claim
reaching a validator may be invalid for that validator, in which case the valida-
tor either discards it (in Step 3) or postpones it (in Step 6). An abstraction
similar to our weak independence was introduced in [21] (Definition 2.6, Com-
mutativity) and used to prove the SEC of their model, but it cannot be directly
used in our formalization.
We prove the following three results for FastSet:
Determinism. The interleaving order in which claims sent by clients are re-
ceived by validators is irrelevant, provided each claim is valid when re-
ceived. That is, the state obtained after their processing is the same.
When JcKs ̸= ⊥, written also c ↓s , we say that claim c ∈ Claim is valid in state
s ∈ State and in that case JcKs is the state obtained after processing the claim.
For example, a claim by Alice making a payment to Bob is valid in a state
where Alice has enough funds, and the state obtained after processing this claim
updates the balances of Alice and Bob adequately. Section 2.2 discusses many
16
other examples of claims. When JcKs = ⊥, written also as c s , we say that state
↚
s cannot process claim c, or c is invalid in s. For example, an invalid claim can be
some payment claim without sufficient balance, or some mathematical theorem
whose proof was not verified by a sufficient number of specialized verifiers, or
some computation claim which has an error (say a division by zero), etc.
Definition 3.2. Extend semantics and validity to claim sequences as expected:
where JϵKs := s and Jc γKs := JγKJcKs. Extend also the ↓ notation to sequences
of claims as expected: ϵ ↓s , and c γ ↓s iff c ↓s and γ ↓JcKs . Also, γ s iff not γ ↓s .
↚
We use ≤ for claim sequence prefix: γ ≤ γ ′ iff γ is a prefix of γ ′ , i.e., there is
some claim sequence τ such that γ τ = γ ′ .
With the notations in Definition 3.2 where γ ≤ γ ′ is witnessed by τ with
γ τ = γ ′ , if γ ′ ↓s then γ ↓s , τ ↓JγKs and Jγ ′ Ks = Jτ KJγKs.
17
may be counter-intuitive at first sight is that an invalid claim sequence is weakly
equivalent to any other claim sequence, whether valid or not—this meaning of
“weak” is, however, well-established in the literature. This implies, in particular,
that weak equivalence is not transitive: if γ ⇓ γ ′ and γ ′ ⇓ γ ′′ then there is
nothing to prevent the existence of a state s such that γ ↓s and γ ′′ ↓s , but γ ′ s
↚
and JγKs ̸= Jγ ′′ Ks. For example, s can be a state where Alice has 11 tokens,
γ and γ ′′ payment claims by which Alice pays Bob 10 tokens and 11 tokens,
respectively, and γ ′ some invalid claim (e.g., Alice pays Charlie 100 tokens).
However, weak equivalence has all the other properties of a congruence:
Proposition 1. Weak equivalence is:
1. Reflexive: γ ⇓ γ;
2. Symmetric: if γ ⇓ γ ′ then if γ ′ ⇓ γ;
3. Compatible: if γ ⇓ γ ′ and c ∈ Claim then c γ ⇓ c γ ′ and γ c ⇓ γ ′ c.
Proposition 2. ∀c, c′ ∈ Claim, if c ∥ c′ then c c′ ⇓ c′ c.
Proof. Let s ∈ State such that c c′ ↓s and c′ c ↓s . Since c c′ ↓s , it follows that
c ↓s . Similarly, since c′ c ↓s , it follows that c′ ↓s . Since c ∥ c′ and c ↓s and c′ ↓s ,
it follows that Jc c′ Ks = Jc′ cKs.
The reverse is not true. For example, if the two claims are the same mutex
acquires, then each of them is valid independently, but they are not valid to-
gether regardless of the order. So c c′ ⇓ c′ c vacuously holds, but c ∥ c′ does not
hold.
The following generalizes Proposition 2:
Proposition 3. Let k ≥ 0 and c, c1 , c2 , ..., ck ∈ Claim such that c ∥ ci for all
1 ≤ i ≤ k. Then
1. ∀s ∈ State, if c ↓s and c1 c2 ... ck ↓s then c c1 c2 ... ck ↓s , c1 c2 ... ck c ↓s , and
Jc c1 c2 ... ck Ks = Jc1 c2 ... ck cKs;
2. c c1 c2 ... ck ⇓ c1 c2 ... ck c.
Proof. Recall that ⇓ is not transitive, so we cannot use Proposition 2 repeatedly.
Property 2 follows immediately from 1. To show 1, let us introduce the
well-defined state notations s1 := Jc1 Ks, s2 := Jc1 c2 Ks, ..., sk := Jc1 c2 ... ck Ks.
Since c1 c2 ... ck ↓s it follows that c1 ↓s . Since c ∥ c1 and c ↓s and c1 ↓s , it
follows that c c1 ↓s , c1 c ↓s , and Jc c1 Ks = Jc1 cKs = JcKs1 , which implies that
Jc c1 c2 ... ck Ks = Jc1 c c2 ... ck Ks = Jc c2 ... ck Ks1 .
Since c2 ... ck ↓s1 it follows that c2 ↓s1 . Since c ∥ c2 and c ↓s1 and c2 ↓s1 , it
follows that c c2 ↓s1 , c2 c ↓s1 , and Jc c2 Ks1 = Jc2 cKs1 = JcKs2 , which implies that
Jc c2 ... ck Ks1 = Jc2 c c3 ... ck Ks1 = Jc c3 ... ck Ks2 . Combined with the equality in
the previous paragraph, this yields Jc c1 c2 ... ck Ks = Jc c3 ... ck Ks2 .
Iterating this process, we obtain that c ck ↓sk−1 , ck c ↓sk−1 , and Jc ck Ksk−1 =
Jck cKsk−1 = JcKsk , which implies our desired equality Jc c1 c2 ... ck Ks = JcKsk =
Jc1 c2 ... ck cKs. Since ck c ↓sk−1 , i.e., c1 c2 ... ck c ↓s , the equality also implies the
only missing part, namely c c1 c2 ... ck ↓s .
18
We next extend weak independence to sets of claims:
Definition 3.5. Sets of claims C, C ′ ⊆ Claim are weakly independent, writ-
ten C ∥ C ′ , iff ∀c ∈ C, ∀c′ ∈ C ′ , c ∥ c′ .
3.3 Interleavings
From here on in the paper, we tacitly assume the following:
Assumption 1. There exists a set Address, whose elements we call addresses
or accounts, which partition the claims into weakly independent partitions:
• Claim = a∈Address Claima
S
example, γ = cAB cBA and γ ′ = cBA cAB , where cAB is a payment of 1 coin
initiated by Alice to Bob, and cBA is a payment of 1 coin by Bob to Alice. Then
γ ↓s and γ ′ s in any state s where Alice has at least 1 coin and Bob has 0.
↚
19
3.4 Correctness
Remark 2. Let us prepend a payment of 1 coin by Charlie to Bob, cCB , to
the claim sequences in Remark 1, that is, consider γ = cCB cAB cBA ≡ γ ′ =
cCB cBA cAB . Then both γ ↓s and γ ′ ↓s in any state s where Alice and Charlie
own at least 1 coin each. Moreover, JγKs = Jγ ′ Ks (both equal to JcCB Ks).
The next theorem shows that this property holds in general, that is, no mat-
ter how the various claims issued (sequentially) by clients are validly interleaved,
the same state is obtained in the end. That means, in particular, that honest
validators are in consensus as soon as they process all the claims, and thus will
behave the same way in the future.
20
|(δ ′ c′ )↾a | ≤ |γ ′ ↾a | = |γ ↾a |. We can now use the induction hypothesis with
γ := δ ′ , γ ′ := γ and c := c′ and obtain γ c′ ↓s . Since c ∥ c′ and γ c ↓s and γ c′ ↓s ,
we deduce that γ c′ c ↓s . Now we can use the induction hypothesis once more
with γ := γ c′ , noticing that |γ ′ | − |γ c′ | = |γ ′ | − |γ| − 1, and obtain γ ′ c ↓s .
Determinism and monotonicity capture correctness of only one validator at
a time: it always converges to the same state and it always allows clients to
settle claims that (they know) are valid. But they do not say anything about
the decentralized network as a whole, specifically about whether all validators
make progress together. For example, could it be that some validators get stuck
due to unfortunate interleavings? The next theorem rules that situation out.
Definition 3.8. Claim sequences γ, γ ′ ∈ Claim∗ are compatible, written γ△γ ′ ,
iff for any a ∈ Address, γ↾a ≤ γ ′↾a or γ ′↾a ≤ γ↾a .
21
this; e.g., if alice wants to transfer 3 native tokens to bob, then she submits
and settles the claim ⟨transfer(bob,3)⟩alice .
⟨C⟩alice′
Here, C is the block that Alice wants to send from account alice’, while
C ′ = verify({alice}, 1); C
is the actual block that Alice needs to submit. The command verify({alice}, 1)
is conditional, which holds iff the message has reached a verifier quorum of size
1, with verifiers from the set {alice}. That is, account alice must also sign it:
Upon receival of the above message, FastSet validators check if the block (i.e.,
C ′ ) is valid in Step 3 of Figure 1. Block C ′ is valid iff C is valid and the
message has been signed by alice. The following messages will not be accepted
by validators because they are not properly signed as specified by verify:
⟨verify({alice}, 1); C⟩alice′ // no verifier sigs
⟨⟨verify({alice}, 1); C⟩alice′ ⟩#{bob} // incorrect verifier sigs
22
However, the following will be accepted and settled:
⟨⟨verify({alice}, 1); C⟩alice′ ⟩#{alice,bob}
Therefore, Alice, as the owner of account alice, can convince everyone else
that all claims submitted from alice’ must be issued from and signed by
her and nobody else. Whoever wants to verify that can simply download all
the settled claims issued by alice’ and verify that all of them have the form
verify({alice}, 1); C. Of course, Alice must remember to wrap her orig-
inal claims C with the verify command, and others must check that all the
settled claims issued by alice’ are wrapped properly. A better approach is
to use contract-based accounts to achieve the above by construction, such that
only properly wrapped claims can ever be generated and issued from the side
accounts of Alice (see Section 4.2).
23
m = ⟨ALICE_SIDE.do(transfer(bob,1)⟩alice
In the do-block, there is the verify({alice},1) command, which holds iff the
message has reached a quorum of size 1 with verifiers from the set {alice};
that is to say, Alice must sign it. It would be redundant and unnecessary to ask
Alice to sign m again like below:
⟨m⟩#{alice} = ⟨⟨ALICE_SIDE.do(transfer(bob,1)⟩alice ⟩#{alice}
Instead, let us define the intended semantics of verify such that the signa-
ture from the original client who issues the claim block, also counts towards
the verifier quorum. Formally, and using the notations in Figure 1: Suppose
a set T of verifiers collectively signs a claim block from client a: ⟨m⟩#T =
⟨⟨c1 c2 . . . ck , n⟩a ⟩#T . Suppose for some 1 ≤ i ≤ k, ci is verify(signers,quorum).
Then ci is valid iff size((T ∪ {a}) ∩ signers) ≥ quorum. That is, the number of
parties who have effectively signed the claim block (i.e., T ∪ {a}) has formed a
quorum against the designated verifiers.
Back to our example. Upon receival of the message
m = ⟨ALICE_SIDE.do(transfer(bob, 1)⟩alice
the validators check that account alice is indeed the creator of the contract
ALICE_SIDE. Then, they instantiate the do-block with transfer(bob,1), and
obtain the actual block:
verify({alice},1); transfer(bob,1)
Next, the FastSet validators will process this block as if it is issued from account
ALICE_SIDE, which causes them to verify that m satisfies verify({alice},1).
Since m is signed by Alice, it satisfies the specified quorum. FastSet validators
will transfer 1 token from ALICE_SIDE to bob if the balance is sufficient.
The side account ALICE_SIDE ensures that
• It has a separate account state from Alice’s main account alice;
• It can only issue the prescribed do-blocks, which require a verification
signature from alice via the verify-command.
Before we wrap up, we point out that for side accounts, we can actually
remove the verify-command from the ALICE_SIDE contract, because only the
contract creator can call the contract blocks. Thus, only Alice can call the
do-block of ALICE_SIDE. Even if Charlie gets Alice to sign the message:
⟨⟨ALICE_SIDE.do(transfer(bob,1)⟩charlie ⟩#{alice}
the validators will not accept it because charlie is not the creator of ALICE_SIDE.
The above attempt message will be rejected/ignored before the do-block gets
instantiated. The simplified side account contract is shown below:
ALICE_SIDE_SIMPLE : ⟨contract( do(C) {C} )⟩alice
As we will show in the next section, the verify-command will become critical
and essential for writing multi-sig contracts that require multiple signatures.
24
4.3 Multi-Sigs
A multi-sig account is a contract-based account that has its own separate ac-
count state but can only issue claims that require a quorum of signatures from
multiple parties. Multi-sig accounts are extensions of side accounts. Below, we
present various possible ways of creating multi-sig accounts.
CHARLIE_ABC =
⟨contract( do(C) { verify({alice,bob,charlie},2); C } )⟩charlie
25
Charlie, or someone else. The only thing that matters here is that the template
MULTISIG_ABC can be publicly referenced by Alice, Bob, Charlie and anybody
else, and they can use the template to create their multi-sig contracts.
The three multi-sig accounts ALICE_ABC, BOB_ABC, and CHARLIE_ABC created
above have identical behaviors as the ones created in the previous sub-section.
TEMPLATE_NAME: TEMPLATE_BODY
DYNAMIC_MULTISIG[SIGNERS, QUORUM]:
signers : Set;
quorum : Int;
constructor {
signers := SIGNERS;
quorum := QUORUM;
}
addSigner(user) { signers.add(user); }
26
removeSigner(user) { signers.remove(user); }
updateQuorum(q) { quorum := q; }
instance do(C) { verify(signers, quorum); C }
This template is intended to be instantiated with a set of signers and a quorum
in order to create a contract, as follows:
CONTRACT_MULTISIG =
⟨contract(DYNAMIC_MULTISIG[{alice,bob,charlie},2])⟩authority
The contract above has several fields and blocks. Template DYNAMIC_MULTISIG
is a template for contracts that have two contract fields, signers and quorum,
which will be initialized with the constructor-block when the contract is cre-
ated, and then modified only by the contract creator with the addSigner,
removeSigner and updateQuorum contract blocks. The do block is now an
instance block, indicated by the instance modifier, meaning that it can only
be called by instances of this contract. Since the other blocks, the constructor
and the fields are contract owned, indicated by the missing instance modifier,
it means that instances of this contract can only produce do blocks.
Here is a scenario:
multisig_alice = ⟨instance(CONTRACT_MULTISIG)⟩alice
multisig_david = ⟨instance(CONTRACT_MULTISIG)⟩david
⟨multisig_alice.do(transfer(gov,3); transfer(mom,1))⟩alice
⟨CONTRACT_MULTISIG.removeSigner(alice)⟩authority
⟨multisig_david.do(transfer(alice,2))⟩david
In words, the authority creates a CONTRACT_MULTISIG contract by instantiating
the DYNAMIC_MULTISIG template, where 2 out of alice, bob, and charlie need
to sign any do block initiated by instances of this contract. Alice and David
want to interact with this contract, so they create instances of it. Alice submits
one do block for verification, which only requires one more signature from bob
or charlie (because it already has the signature from alice). The authority
removes Alice from the list of signers, without modifying the quorum, which
means that David’s last do block requires signatures from both bob and charlie
(in addition to david’s own signature). If we only want to allow signers to
submit do blocks, we can modify the do block in the template as follows:
instance do(C) {
instance.owner in signers;
verify(signers, quorum);
C
}
All participants in this contract and its instances have to be careful to make
sure that their claims can be settled, and are settled correctly as intended. For
example, if authority removes too many signers without modifying the contract
27
quorum field, then the instances’ do blocks will never get validated. Or more
subtly, if an instance issues a do block before a contract update by authority is
settled, then different validators may impose different verification requirements
and thus the instance’s message may not accomplish FastSet validator quorum.
The above is actually a consequence of a deliberate language design decision:
setl does not enforce the weak independence property! This is an important
decision which we expect other implementations of FastSet to also make, so it
is worth discussing. Recall from Sections 2.2 and 3.2 that claims that access a
shared location, where one is a write, may not be weakly independent. This is
precisely what happens in the DYNAMIC_MULTISIG script above: claims issued
by the contract owner authority may update/write the set of signers and their
quorum, while instances of the contract read those shared values in their verify.
This can lead to non-deterministic and even unintended validator behaviors.
Indeed, suppose that david (and/or his proxy) conspires with corrupted alice
to get her signature on a transaction that the authority and charlie would
not approve if they knew alice were corrupted. Then david (and/or his proxy)
submits the transaction claim when alice is still a signer (assume that bob signs
as well), collects and aggregates validator signatures (Steps 3, 4 and half of 5 of
the protocol in Figure 1), but does not broadcast the quorum back to validators
until after authority’s removal of alice is settled. Then david broadcasts the
message signed by alice and settles it against authority’s and charlie’s will.
Some may argue that the above is not a downside, but a feature of FastSet.
Indeed, it may very well be the case that david (and/or his proxy) was not
malicious but was simply disconnected right before broadcasting the validator
quorum. Implementations of FastSet may choose to also include the validator
aggregated signature together with the message in v.presettled in Step 6 of the
protocol, and then recheck the validity of verify in Step 7. We choose to not do
it in setl, for efficiency reasons, but recommend developers and their auditors
to think through such scenarios like above and handle them programmatically
as desired. For example, the script can require instances to also provide the
current signers and their quorum, and then check them against the contract’s:
instance do(C, mysigners, myquorum) {
mysigners == signers and myquorum == quorum;
verify(signers, quorum);
C
}
This adjustment guarantees that the instance’s message is invalid if the authority
changes the signers or the quorum before the instance’s message is settled. While
this prevents (potentially) unintended claims to be settled, unfortunate inter-
leavings of the various concurrent actions can lead to an instance being stuck
forever as invalid. This is further discussed and resolved in Section 4.17.
28
4.4 Kudos
Suppose that a team wants its members to give each other kudos, which come
with a monetary value. Then every member, say Alice, can create a contract
for collecting her kudos as follows:
KUDOS_ALICE_CODE:
k : Int;
constructor { k := 0; }
instance pay(n) { transfer(alice, n); k += n; }
KUDOS_ALICE = ⟨contract(KUDOS_ALICE_CODE)⟩alice
Here KUDOS_ALICE_CODE is a template and KUDOS_ALICE is the actual contract.
Each member who wants to give Alice kudos would then create an instance
of the contract and then issue a pay block. For example, Bob settles an instance
that is specifically created for Bob to give Alice kudos:
kudos_alice_bob = ⟨instance(KUDOS_ALICE)⟩bob
29
KUDOS_ALICE = ⟨contract(KUDOS_CODE[alice])⟩lead
KUDOS_DAVID = ⟨contract(KUDOS_CODE[david])⟩lead
30
In FastSet, a programming language (PL) or a virtual machine (VM) is a
particular claim. The unique identifier, or the hash, of a PL or VM claim serves
as the contract between developers writing and claiming programs in that PL
or VM, and the verifiers who verify executions of such programs. There is no
unique way to claim a PL or VM; here are some possibilities:
PYTHON_CODE:
lang : String;
bin : Bytes;
...
constructor {
lang := "Python 3.13.3";
bin := bytearray;
...
}
PYTHON = ⟨contract(PYTHON_CODE)⟩Python
and
EVM_CODE:
lang : String;
spec : String;
...
constructor {
lang := "EVM Prague";
spec := "module EVM is ...";
...
}
EVM = ⟨contract(EVM_CODE)⟩EF
The language name and an implementation, such as a binary or source code in
some other language, or a formal specification of the language are expected to
be included as fields and initialized appropriately. Ideally, such PL/VM claims
are non-ambiguous and self-contained, so that they serve as the source of truth
for what the language is. Also ideally, the owners of the accounts making such
claims are publicly known authorities, such as the inventors or co-inventors of
those languages, and they should also set up other authorities as claim verifiers,
for example the language standards committee members, with high quorums to
be reached. However, all these attributes are external to FastSet.
31
Programs can be settled similarly to languages, as claims containing the
program as a field. The precise PL/VM is important, because the same program
can have different meaning in different languages or versions of them. Again,
FastSet does not prescribe any specific ways, but here are some examples: let
FIBONACCI_CODE:
lang_addr : Address; // the address at which Python is settled
pgm : String;
...
constructor {
lang_addr := PYTHON;
pgm := "fibonacci(n)...";
...
}
FIBONACCI = ⟨contract(FIBONACCI_CODE)Joe
be a Python program settled by Joe, where PYTHON is the unique ID/name of
the claim that settles the specific Python language being used, and the field pgm
holds the actual program code. Let
UNISWAP_CODE:
lang_addr : Address; // the address at which EVM is settled
pgm : String;
...
constructor {
lang_addr := EVM;
pgm := "swap...";
...
}
UNISWAP = ⟨contract(UNISWAP_CODE)Hyden
where EVM is the unique ID/name of the claim that settles the specific EVM
version used in this program. Both PYTHON and EVM can be settled as shown in
Section 4.6. Similar to PL/VM claims, program claims can and should also be
set up to have verifiers, e.g., security (or ideally formal verification) auditors.
As we will shortly discuss in Section 4.8, program execution claims consist of
a field holding the program claim, e.g., FIBONACCI or UNISWAP above, as well as
the input to the program and the states before and after the execution. Clients
can make multiple such computational claims and are responsible for properly
composing them in order to avoid divergence (or double spending). However,
such computational claims need to be verified. The client only proposes the
computational claims, but cannot guarantee their integrity. In fact, the client
may generate the claim using a cheap untrusted device, or even no device at all
if the result can be inferred differently or guessed. Or can use powerful machines
and parallel infrastructure that generates hundreds of computational claims per
second, like some Layer 2 (L2) blockchains do.
32
As soon as a PL/VM that attracts developers is settled, the demand for
verifiers of computations in that PL/VM should increase. Fortunately, all the
verifiers need to know is the PL/VM itself, so they can offer their services as
verifiers as soon as the PL/VM is settled. What could be appealing for verifiers
is the fact that they can verify computations generated by various programs at
the same time, to keep their hardware effectively used. For example, Ethereum
has 1.2 million nodes currently, each of them verifying the same EVM blocks of
transactions. It takes less than 100ms to verify a block, yet the block time is 12s,
so they are used at under 1% of their capacity. Supposing that these 1.2M nodes
subscribe as FastSet verifiers for EVM, they would likely be able to service all
the demand for EVM program executions across all the blockchains, both L1s
and L2s. Moreover, such an efficient infrastructure could also suggest new, more
efficient architectures and implementations of blockchains (see Section 4.16).
33
can bring a large number of verifiers to the market for all applications to use,
by creating the following contract:
EVM_VERIFIERS: {V1,V2,...,V1000000}
VERIFIED_EVM =
⟨contract(VERIFIED_COMPUTE[EVM, EVM_VERIFIERS, 1000])⟩EF
We are aware of the abuse of syntax in the above when we create the contract
VERIFIED_EVM, where the parameter EVM_VERIFIERS should be used not as a
value, but as an address. One must dereference EVM_VERIFIERS and create the
contract using the actual value {V1,V2,...,V100000}. In this design docu-
ment, we decide to live with this abuse of syntax and treat it in a separate
document where we nail down a formal specification of the setl language and
its implementations.
Applications can now create instances of VERIFIED_EVM and generate com-
putational claims using the compute-block. By definition and by construction,
all computational claims are built as follows:
claim("compute", ...)
but this is merely a convention that all the participating clients, especially
applications and verifiers, must agree upon.
An application, such as a Layer 2 blockchain L2chain, can now create an
instance L2ver standing for “L2 verified”:
L2ver = ⟨instance(VERIFIED_EVM)⟩L2chain
and then issue computational claims by signing messages like these:
⟨L2ver.compute(pgm1, input1, s1, claims1, s2, proof1)⟩L2chain
⟨L2ver.compute(pgm2, input2, s2, claims2, s3, proof2)⟩L2chain
Each of these messages must also be signed by a quorum of the verifiers for
FastSet validators to successfully settle them:
⟨⟨L2ver.compute(pgm1, input1, s1, claims1, s2, proof1)⟩L2chain ⟩#T1
⟨⟨L2ver.compute(pgm2, input2, s2, claims2, s3, proof2)⟩L2chain ⟩#T2
Here, T1 and T2 are two (possibly different) sets of 1000 verifiers in EVM_VERIFIERS.
The actual claims that FastSet validators will settle upon receiving the above
messages would be the following:
⟨claim("compute", EVM, pgm1, input1, s1, claims1, s2, proof1)⟩L2chain
⟨claim("compute", EVM, pgm2, input2, s2, claims2, s3, proof2)⟩L2chain
Computational claims like above are meaningless for FastSet, but their set-
tlement indicates that the verifiers approved them. Note that the verification
of computational claims and calls into the L2ver.compute blocks take place in
the verifiers, and thus outside FastSet.
We now explain the intuition behind the arguments of the compute claims:
34
• "compute" - a type code that indicates that this is a computational claim;
• LANG - the programming language, such as EVM, settled as in Section 4.6;
• pgm - the program/code that is executed, settled as in Section 4.7;
• input - the input of the program;
35
⟨L2ver1.compute(pgm1, input1, s1, claims1, s2, proof1)⟩L2ver1
⟨L2ver2.compute(pgm2, input2, s2, claims2, s3, proof2)⟩L2ver2
If there are sufficient verifiers to choose from and they are incentivised to reach
quorum, an application can always spawn multiple instances to process its tasks
in parallel. In the above, L2chain creates two VERIFIED_EVM instances and thus
doubles the speed of computational claims being verified and settled. Computa-
tional claims can now be generated sequentially, but submitted for verification
and settlement in parallel, by the two separate instances, without waiting for
one to be verified and settled, before the other does so. Once these two com-
putational claims are verified by possibly two different quorum sets of verifiers,
the following claims will be eventually settled on FastSet:
L2SEQ = ⟨contract(FINALIZE_SEQUENTIALLY[L2genesis])⟩L2chain
using the following template, where L2genesis is the genesis state of this L2:
FINALIZE_SEQUENTIALLY[GENESIS_STATE]:
state : Bytes;
constructor {
state := GENESIS_STATE; // the genesis state of the app
}
step(pgm, input, s, claims, s’, proof) {
state == s;
isSettled(
claim("compute", EVM, pgm, input, s, claims, s’, proof)
);
claims; // application claims are actually processed here
state := s’;
}
getState() { claim("currentState", state); }
36
On initialization, L2SEQ is at state L2genesis, which is its genesis state.
Then, L2SEQ can make one step forward by calling and settling the step-block.
The step-block will eventually update the state of L2SEQ to a new state, if there
is a computational claim that has been settled (and thus verified) whose “start
state” s is identical to the current state of L2SEQ. Additionally, the effects of the
claims are now being processed by FastSet validators, before the new state is
updated to s’. Because the state field of the instance is not accessible to other
clients who may want to query it, the contract also includes a getState block
that allows the instance to claim its current state.
Note that it is possible that multiple computational claims are verified and
settled on FastSet and all of them share the same initial state, say s, but lead
to different final states, say s’, s’’, and so on. This is similar to a forking
situation. It is fine to have forking computational claims because it is indeed
the case that all of them are correct, computationally speaking. However, the
application must pick one and only one of these computational claims to make
a step, from the L2SEQ instance. Once a computational claim is picked, a step
is made, and a new state, say s’, is reached, the other computational claims
with the initial state s can no longer be applied to step further.
The isSettled command checks if a claim has been settled. Every FastSet
validator maintains a settled claim set. New claims, once they are validated
and settled, will be added to this set, but no claims can be removed from the
set. In this way, the settled claim set forms an CRDT (see Section 2.2) w.r.t.
claim settlement. In addition, isSettled is a side-effect free Boolean check on
the monotonically increasing settled claim set, so once it is valid, it continues
to stay valid. It thus guarantees that using isSettled to build a conditional
claim does not break the weak independence property.
We glossed over many details here, such as how the programs and their inputs
are represented, whether the states are given in clear or as indexes in an agreed
upon data availability (DA) layer, even over what constitutes a proof and how
applications generate the claim arguments first place in order to present them to
the verifiers. All these are FastSet-external conventions which are expected to
be agreed upon in advance among the participating stakeholders (language com-
mittees/standards, applications, execution service providers, provers, verifiers,
etc.).
In Section 4.16 we build upon the verified computing approach discussed in
this section and show how an actual blockchain can be implemented in setl.
4.9 Voting
Voting is common on blockchains, yet it is an inherently parallel activity in
which the order of votes is irrelevant, making FastSet ideal for it. An authority
needs to set up and create a vote poll and then allow voters who are verified to
vote. Finally, the authority should announce the results.
Let us consider a simple voting scenario, where the authority, say gov, who
is also responsible for verifying the voters, wants to accumulate MIN valid votes
to pass some MOTION. Then gov can create a contract using the setl script:
37
BASIC_VOTE[MIN]:
voted : Set; // users who voted
constructor {
voted := {};
}
instance constructor {
not(instance.owner in voted); // guard: must be true
verify(contract.owner, 1); // authority verifies voter
voted.add(instance.owner);
}
passed() { size(voted) >= MIN; }
MOTION = ⟨contract(BASIC_VOTE)⟩gov
⟨instance(MOTION)⟩alice
⟨instance(MOTION)⟩bob
⟨instance(MOTION)⟩charlie
...
38
and now alice can settle ⟨motion_alice.voted()⟩alice and use its certificate
as proof—here motion_alice is the instance ⟨instance(MOTION)⟩alice .
Similarly and for the same reasons, it is impossible for alice to know with
certainty whether her vote was actually counted, that is, that it contributed to
claim ⟨MOTION.passed()⟩gov being settled. This is the case even if alice set-
tled a claim ⟨motion_alice.voted()⟩alice like discussed above chronologically
before ⟨MOTION.passed()⟩gov on some validator(s) that she uses to observe the
voting process. Since chronological order is useful in cases like above, and for
other reasons, in Section 4.14 we propose to extend FastSet and setl with claim
timestamping: claim issuers timestamp each claim block, and validators check
and approve the timestamp.
Finally, it is insightful to note that although it may appear that a (malicious
or not) voter can attempt to create two different instances and submit them
concurrently to the validators in the hope that they both get settled, that is
not possible thanks to the fact that each account has a nonce and at most
one message is allowed in a validator’s pending for any given account (steps 3
and 4 in Figure 1). A voter attempting to create two concurrent (same nonce)
instances would therefore risk to get their account stuck. A voter attempting
to create two concurrent (same nonce) instances would therefore risk to get
its account stuck. There is nothing to prevent them to attempt to create two
instances in sequence, each issued with a different nonce, but in that case they
are processed in order and the second one will fail on all validators as soon as
they processed the first one, as intended. There is nothing to prevent voters
from attempting to create two instances in sequence, each issued with a different
nonce, but in that case they are processed in order and the second one will fail
on all validators as soon as they processed the first one, as intended.
4.10 Games
Generally speaking, games, especially game scores and game data that are veri-
fiably produced by a set of cooperating participants, can be stored in a contract,
where each participant creates an instance only to claim their next move.
As an example, here is a contract for playing chess. The contract is para-
metric in the two players, WHITE and BLACK, as well as in an ARBITER whose role
is to verify the moves. The players alternate and each makes a move according
to a convention agreed upon with the arbiter, e.g., using the algebraic nota-
tion [6]—which also includes notations for the end of game (winner or deuce).
CHESS[WHITE, BLACK, ARBITER]:
game : String;
turn : String;
constructor {
game := "";
turn := "WHITE";
}
instance white(move) {
39
turn == "WHITE"; instance.owner == "WHITE";
game += move; verify(ARBITER,1); turn := "BLACK"; }
instance black(move) {
turn == "BLACK"; instance.owner == "BLACK";
game += move; verify(ARBITER,1); turn := "WHITE"; }
To play a game, alice and bob pick an arbiter charlie (say a service provider
accredited by FIDE) and create the following contract
GAME1723 = ⟨contract(CHESS[alice, bob, charlie])⟩any
It is irrelevant who creates the contract—can be any of the three participants,
or some other application. Once created, the contract will store the shared game
and a shared flag turn used to enforce player alternation. To play GAME1723,
alice and bob need to create (empty) instances of it, say
crashing_bob = ⟨instance(GAME1723)⟩alice
alice22 = ⟨instance(GAME1723)⟩bob
4.11 Escrow
The prototypical escrow involves three parties: a seller, a buyer, and an agent.
The buyer wants something, say an object, from the seller, which may take
some time and effort from the seller. So the seller does not want to send the
40
object before knowing that the buyer will pay. The buyer on the other hand
does not want to send the funds to the seller, because the seller may not send
the object. Let’s assume a simple escrow controlled by an agent who decides
where the funds go, but without direct access to the funds. The buyer sends
the money to the escrow, and the agent pushes one of two buttons: release the
funds to the seller (when the seller sent the object), or refund the buyer (when
the seller failed to send the object). The agent needs to create an appropriate
instantiation of the following ESCROW contract:
ESCROW[SELLER, BUYER]:
amount : Int;
isFunded : Bool;
constructor {
amount := 0;
isFunded := false;
}
instance deposit(v) {
instance.owner == BUYER;
not isFunded;
v > 0;
transfer(contract,v);
amount := v;
isFunded := true;
}
release() {
isFunded;
transfer(SELLER,amount);
isFunded := false;
}
refund() {
isFunded;
transfer(BUYER,amount);
isFunded := false;
}
For example, the agent creates the ESCROW_AB contract for seller alice and
buyer bob. The buyer has to create an instance of it and then send the funds:
ESCROW_AB = ⟨contract(ESCROW[alice,bob])⟩agent
bob_pays_alice = ⟨instance(ESCROW_AB)⟩bob
⟨bob_pays_alice.deposit(10)⟩bob
The funds are now locked in the escrow and the only way to unlock it is for the
agent to either release it to the seller, or refund it back to the buyer:
⟨ESCROW_AB.release()⟩agent or ⟨ESCROW_AB.refund()⟩agent
41
Same like in Section 4.10, weak independence holds. Indeed, there can only
be two accounts at a time that issue claim blocks, namely the contract owner
(agent above) and the seller (bob above). The buyer does not interact with
the contract. The boolean flag isFunded mutually excludes either the contract
owner’s claim or the seller’s, by making either one or the other invalid. This
way, it can never be the case that both the contract owner’s claim and the
seller’s claim are valid, so the weak independence property holds vacuously.
42
All the non-native tokens and assets are stored in the contract, in its balance
field. Whenever bob wants to get access to the 100 tokens that alice transferred
to him, he can simply create an instance:
bob_USDC = ⟨instance(USDC)⟩bob
43
4.13 Sequencing
Sequencing of actions that may come in arbitrary order is important for a series
of applications, like blockchains. We here show a simple example for how a set of
users can register to a sequencing contract, which imposes a total order on them.
How the order is picked is left entirely to the contract owner here, although in
practice users of such a contract may require the use of VRFs, timestamps, or
other verifiable means.
SEQUENCING:
registered : Set;
sequenced : List;
constructor {
registered := empty;
sequenced := nil;
}
instance register() {
not(instance.owner in registered);
registered.add(instance.owner);
}
pick(a) {
a in registered;
registered.sub(a);
sequenced.next(a);
}
To register, a user creates an instance of the contract. Only one registration
per user is allowed here, for simplicity, which means that any attempt by a user
to register twice will result in the second claim being invalid. It is important
to use instance.owner instead of instance above, because each instance of
the contract will have a different instance number. At any given moment,
the contract owner picks a registered user and moves it from the unordered
registered to the ordered sequenced. Like any claim block, pick(a) may be
valid for some validators and invalid for others (those which have not received
a in their registered yet). The contract may need to resend the claim block
to validators until quorum is achieved.
Note that although two different accounts can read and write a shared lo-
cation, the contract does not violate the weak independence assumption that
guarantees the correctness of the protocol. The interesting case is when the
contract owner issues a claim block pick(a) while an instance.owner issues a
register() block. Suppose that both claims are valid. Then it must be the case
that a and instance.owner are distinct, because the former is in registered
while the latter is not. This means that the order in which the instance.owner
is added to registered and a is removed from registered is irrelevant.
Orthogonally to weak independence, it is also interesting to analyze the case
when a == instance.owner above, which can happen when a user a was se-
lected by the contract owner, but the user attempts to re-register before pick(a)
44
was settled. The result is that on some validators the user manages to re-register
(namely those which have already processed pick(a)), while on the others the
re-registration is invalid. The user has the option to re-submit the re-registration
claim to the latter validators and will still be able to achieve quorum, i.e., their
account is not stuck. Of course, the user will have to re-pay those validators
the settlement fee, to avoid DOS attacks.
connections (WWAN) might have delays between 100 and 300 ms.
45
then use the timestamp to prove that the activity happened on time, or to even
attempt to order the activities chronologically. Here is an example contract
using time to pick the user who registered chronologically first. In case of a tie
on time, the user whose instance ID/name is smaller wins.
FIRST[START]:
first : Int;
time_first : Int;
instance_first : Int;
constructor {
time_first := infinity;
instance_first := 0;
}
instance constructor {
time > START;
time < time_first
or (time == time_first
and instance < instance_first);
first := instance.owner;
instance_first := instance;
time_first := time;
}
Here we assumed that claim blocks are executed atomically, that is, different
block claims accessing the same shared variable will not interleave their claims.
This is important, for example, in the constructor above where the shared vari-
ables time can be read and written by various instances at the same time. The
overall effect on the shared variables will be the same once all instances are
settled regardless of the order in which the instances are processed, but it is
important that only one instance at a time accesses the shared variables. We
are aware that the atomicity of claim blocks is a very strong assumption and
plan as future work to weaken it by adding synchronization objects (mutexes).
4.15 Auctions
Auctions tend to be complex contracts, where a set of bidders submit their bids
for an item. The auctioneer keeps the highest bid and the highest bidder receives
the item, while the other bidders redeem their funds that were out-bidden.
There are many variations of auction contracts, which, to our knowledge, fall in
two broad categories when it gets to how the bidders redeem their funds: either
they do it themselves, usually by calling a withdraw function when permitted, or
the auctioneer sends each of them their bids back when the auction ends. None
of these approaches is possible within setl, due to its deliberately restricted
nature. Indeed, there is no way for an account to withdraw any funds from
another account, at least not with the current implementations of the native
transfer and the contract transfer_token block in Section 4.12: the other
46
account has to send the funds explicitly. Also, setl currently has no mechanism
to iterate through all the keys of a map (this might be needed, eventually).
We here present a different type of an auction contract, which takes full
advantage of the parallel nature of FastSet. The key insight is that the highest
bidder pays the previously highest bidder back, and sends the difference to the
contract. This way, the contract only locks the auctioneer’s item and the highest
bid, so there is no need to send any funds back to the out-bidden participants.
AUCTION[ITEM,BIDDING_TIME]:
stopBiddingTime : Int;
highestBidder : Address;
highestBid : Int;
constructor {
ITEM.transfer_token(contract, 1);
stopBiddingTime := time + BIDDING_TIME;
highestBidder := contract.owner;
highestBid := 0;
}
instance bid(amount) {
time <= stopBiddingTime;
if (amount > highestBid) {
transfer(highestBidder, highestBid);
transfer(contract, amount - highestBid);
highestBidder := instance;
highestBid := amount;
}
}
instance withdraw(amount) {
transfer(instance.owner, amount);
}
end() {
time > stopBiddingTime;
ITEM.transfer_token(highestBidder.owner,1);
transfer(contract.owner, highestBid);
}
The auctioneer creates the contract above, at the same time sending it the
ITEM for bidding, as well as a BIDDING_TIME for how long bidding is allowed,
e.g., AliceTicket = ⟨contract(AUCTION[ticket_item,1000])⟩alice . For sim-
plicity we assume only one item, which is a token as in Section 4.12. This item
is locked in the contract until the auction ends. The contract initializes the
highestBidder as the contract owner, alice in our case, so the contract owner
can redeem the item in case nobody bids on it during the specified period.
47
Bidders create instances of the contract, send funds to their instances in order
to bid them, initiate bid blocks through their instances, and finally withdraw
from their instances whatever funds were not used for bidding, e.g.,
auction3 = ⟨instance(AliceTicket)⟩bob
⟨transfer(auction3,100)⟩bob — bob sends 100 to its instance
⟨auction3.bid(25)⟩bob — bob sends 25 to AliceTicket (not alice)
tk_alice = ⟨instance(AliceTicket)⟩charlie
⟨transfer(tk_alice,50)⟩charlie — charlie sends 50 to its instance
⟨tk_alice.bid(30)⟩charlie — sends 25 to auction3 and 5 to AliceTicket
⟨auction3.bid(40)⟩bob — sends 30 to tk_alice and 10 to AliceTicket
Bidding stops after the allowed time, and then the auctioneer (the contract
owner) issues an end() block which sends the item to the highest bidder and
the paid amount from the contract to the contract owner. For example,
⟨AliceTicket.end()⟩alice — sends 40 to alice and ticket to bob
At any moment during or after the auction ends, the bidders can withdraw any
available funds from their instances, e.g.,
⟨tk_alice.withdraw(50)⟩charlie — charlie withdraws its funds
⟨auction3.withdraw(60)⟩bob — bob withdraws everything left
The weak independence assumption that guarantees the correctness of Fast-
Set is still obeyed by the contact above, but it is less obvious than in the previous
examples. Before we show that weak independence holds, let us first illustrate
the power, but also the complexity of concurrency, in order to appreciate the
critical role that weak independence plays. The potential problem is that the
shared field highestBid can be both written and read by different instances.
Consequently, multiple bidders may bid concurrently and, although their indi-
vidual bids get quorum, they may potentially not be able to settle. For example,
in the scenario above suppose that both bob and charlie send their first bids at
the same time and the validators receive their bid claims at the same time. Both
claims are valid in Step 3 of the FastSet protocol (Figure 1) regardless of which
is processed first, because at that step the validator states are not modified. All
validators therefore sign both claims and quorum is achieved for both.
Suppose now that half of the validators receive/process the two bids in one
sequence in their Step 7, say bob first and charlie second, while the other half
in the other sequence, charlie first and bob second. In both cases the contract
will hold 30 tokens, charlie will be the highest bidder, bob’s instance balance of
100 tokens is unaffected, and charlie’s instance balance is 20 tokens. However,
in the second case, bob’s bid never took place, in the sense that the body of
the conditional statement was not executed. But, importantly, bob’s bid block
was still valid, so in the end all validators are in the same state and with the
same claims settled. Since bob’s instance balance was not affected on any of
48
the validators, his next ⟨auction3.bid(40)⟩bob correctly outbids charile on
all validators. We leave it as an exercise to the curious reader to notice that
weak independence would be violated if we replaced the conditional statement
with a guard that invalidated the block when the amount was not higher than
the highest bid. In Section 4.17 we discuss this case in more depth and propose
an extension of FastSet that would work with such examples where weak inde-
pendence is allowed to be temporarily broken. We also leave it as an exercise
to the reader to notice that the weak independence assumption would also be
violated if the previously highest bid would be returned directly to the owner
of the instance, instead of to the instance itself. We discuss this case in more
depth in Section 4.17 as well.
Let us now prove that the AUCTION contract obeys weak independence. The
interesting case is when two independently highest bids are submitted at the
same time. Regardless of the order in which they are processed in a given state,
once both are processed the state is the same: the currently highest bidder’s
instance is paid back, the winner of the two bids becomes the highest bidder,
and the state of the loser of the two bids stays unchanged.
49
claims together with submitting transactions to the chain, and also allow blocks
to settle claims together with updating the chain state. This opens a broad
range of possibilities. For example, chain users and accounts can hold their
assets directly on FastSet as non-native tokens (Section 4.12). Also, applications
can use FastSet for everything that can take advantage of parallelism, and use
the chain for everything that requires sequential execution. For example, an
AI agentic application can use FastSet for storing AI agent states, for verifiable
communication between agents, and for transferring assets from one agent to
another, all at the speed of light, but use a chain (or more) for trading.
CHAIN[LANG]
mempool : Set;
chain : List;
next_block : List;
constructor {
mempool := {};
chain := [];
next_block := [];
}
pick(trans) {
trans in mempool;
mempool.sub(trans);
next_block.next(trans);
}
50
For concreteness, let us assume the verifiable compute context discussed in Sec-
tion 4.8, where EVM is settled as a language claim and VERIFIED_COMPUTE is a
contract that allows its instances to make use of a set of specialized verifiers to
verify compute claims using EVM and then settle them as claims of the form:
claim("compute", EVM, pgm, input, s, claims, s’, proof)
evmchain = ⟨contract(CHAIN[EVM])⟩chain
For simplicity, we here assume that chain is a normal FastSet account, which
has the freedom to pick any order of transactions. For increased trust, security
and decentralization, in practice this chain account may be controlled by a
multi-sig, or by an aggregated signature, or at a minimum the pick(trans)
blocks may require verification from an approved set of qualified verifiers. We
keep the discussion high-level and do not dive into these concrete options here.
To use the blockchain, FastSet users need to create instances of evmchain:
evmchain_alice = ⟨instance(evmchain)⟩alice
The instances give them access to submit blocks, for example:
⟨evmchain_alice.submit(alice_trans, transfer(evmchain,1)⟩alice
Above, alice submitted a transaction on the chain, alice_trans, and 1 token
to the contract. This could be, for example, a bridge transfer, which mints
1 token in alice’s account on evmchain. We are not concerned with the in-
tegrity or semantics of the specific chain transactions here. These can be ver-
ified by the chain contract itself, or by its verifiers, or can be generated to
be semantically-correct-by-construction using variants of submit blocks, e.g.,
⟨evmchain_alice.bridge(1)⟩alice (for brevity, we do not define bridge here).
The mempool holds the set of pending chain transaction intents, that is, the
transactions submitted by its users which were not executed by the chain yet.
51
breaking weak independence and propose an extension to languages like setl
that could ameliorate the problem in some, but certainly not all, situations.
First, consider the apparently innocent change in the AUCTION contract in
Section 4.15, where the new highest bidder pays back the previously high-
est bidder owner, instead of their instance. That is, replace instance with
instance.owner in the bid block (and remove .owner in the end block). Con-
sider the same scenario discussed in Section 4.15, where bob and charlie sub-
mit their bids concurrently, and half the validators process bob first and then
charlie, while the other half charlie first and bob second. In both cases the
contract will hold 30 tokens, charlie will be the highest bidder, and charlie’s
instance balance is 20 tokens. However, in the first case bob’s balance is 25
tokens more while bob’s instance balance is 75 tokens, while in the second case
bob’s balance is unaffected while bob’s instance balance is 100 tokens. It may
look like there is no problem, because bob’s total balance in both its account
and in its instance stays the same, and he always has the option to withdraw
from the instance. However, bob effectively lost 25 tokens, because he will only
be able to get validator quorum on withdrawals of up to 75 tokens. Additionally,
from here on, bob’s account will show different balances on different validators,
without any chance for the validators to ever converge on bob’s balance.
Let us next consider another change to the AUCTION contract in Section 4.15,
where the conditional statement in bid is replaced with its condition as a guard
followed by the same commands. That is, bid is invalid now when the amount is
insufficient, instead of silently valid. Now the case when bob’s bid is processed
after charlie’s is invalid, thus breaking the weak independence assumption.
As a consequence, bob’s bid cannot be settled. In fact, it has no chance to
ever be settled for that half of the validators, regardless of how the auction
proceeds, not even after the auction ends. So bob’s bid will stay forever in
v(auction3).pending for all those validators. Not only is this garbage both
computationally and space-wise for half the validators, but also it locks the
auction3 account at the current nonce on those validators, which means that
auction3 is effectively forever locked together with bob’s 100 tokens, because
it will never be able to achieve quorum on any message anymore.
If bob still wants to outbid charlie he can create another instance of the
contract, say auction4 = ⟨instance(AliceTicket)⟩bob , transfer new funds to
it and then ⟨auction4.bid(40)⟩bob . However, even though in this particular
situation bob has a solution at the expense of losing the 100 tokens locked in
the auction3 instance, the fundamental problem stays and is clearly rather
grave. Any FastSet implementation that allows accounts to disobey the weak
independence assumption, like our setl, should probably also provide a general
resolution for what to do in situations like the above where the monotonicity
property is violated. Otherwise, malicious attackers can easily initiate DOS
attacks on the network by overloading the validators with pending messages
that never settle in Step 7 of the protocol. In setl, we choose the following:
Monotonicity Enforcement Resolution: In Step 7 of FastSet
in Figure 1, if m = ⟨c1 c2 ... ck , n⟩a in v.presettled with v(a).nonce =
52
n and v(a).pending = {m} but c1 c2 ... ck v.state , then increment
↚
v(a).nonce, reset v(a).pending := ∅, and move m to v.settled.
That is, setl tacitly settles the message as if everything was alright, but it
ignores its undefined effects on the state. In particular, the nonce of the account
is incremented, this way allowing the account to continue to submit claims—
may want to do so to recover. Note that the account has already paid the
settlement fee, so validators do no unpaid work. In other words, setl takes
the healthiest decision for itself and leaves it to the apps to semantically fix the
problems resulting from using contracts that break weak independence.
setl’s monotonicity enforcement resolution makes the modified auction con-
tract work as intended: fast, correct, and minimal on resources. Indeed, each
bidder submits their bids unrestricted by anything but the deadline, as it should
be, without any sequencing or waiting required, as it should be, and only the
contract needs to store data, and only two integers: the highest bidder and its
amount. Correctness means that from all validators’ perspectives, the highest
bidder gets the item from the auctioneer and all other bidders get their money
back. It follows easily by induction. Suppose that the property holds for n
bidders, and consider one more bid. Let v be any validator. If the new bid is
higher than the previously highest bid for v, then the bid claim block will set
the new bid as the highest and the previously highest bidder will be transferred
its money back by the new bidder (which can also be the same bidder). So
the inductive property holds in this case. If the new bid happens to be lower
than the previously highest bid for v, which can happen because of the receive
and settlement order in Step 7 of the FastSet protocol on v, then the bid claim
block will be invalid and, therefore, by the monotonicity enforcement resolution
above, will not change the state of v (but it will increase the nonce of the bidder,
so it can continue to participate in bidding). So the property holds in this case,
too.
The second modified auction contract in this section is a good example il-
lustrating why implementations of FastSet, like our setl, may choose to not
enforce the theoretical weak independence requirement. While weak indepen-
dence guarantees the correctness of the protocol in all situations and for all
contracts, it may sometimes be too restrictive. If implementations do not en-
force weak independence statically, like our setl, then users should be cautious
and not use contracts they do not trust or which are not appropriately audited.
5 Conclusion
This paper introduced FastSet, a replica-based distributed protocol for embar-
rassingly parallel settlement of claims. A claim is any statement that comes with
a proof, such as a payment or more generally a digital asset transfer, a realized
blockchain transaction or more transactions that form a block, an execution of a
program in a programming language or virtual machine, an AI model inference
or fine tuning, a TEE execution, a vote, an auction bid, among many others.
A proof is anything that is verifiable and is accepted by the application or
53
user/account that uses the claim: a signature (simple, aggregated, TEE, etc), a
cryptographic/ZK proof, a mathematical proof, a formal semantics derivation,
a program (re)execution, and so on. The verification of all proofs which are
not signatures is deferred to special service providers, called verifiers which are
account holders like any other applications/users. The replicas, called valida-
tors, only validate signatures, regarded as the simplest and fastest way to check
proofs, and settle the claims. Each claim can be verified, validated, and settled
optimally: independently and in parallel with any other claim. The validators
need not communicate with each other, so there is no consensus in the strict
sense of the word as used in blockchains. Specifically, FastSet is not strongly
consistent [12], but it is strongly eventually consistent [21].
FastSet drew inspiration from three relatively recent works, published after
2019, which in our view have brought irrefutable evidence that strong consis-
tency in general, and blockchains in particular, are not necessary in order to
prevent the double spending attack problem: the consensus number of a cryp-
tocurrency [9], FastPay [3], and POD [2]. These works demonstrated that the
peer-to-peer decentralized payment system challenge that was notoriously solved
by Bitcoin [16] and has stimulated more than a decade of sustained research,
engineering and investments advancing the blockchain technology to what it is
today, can also be solved differently. Quite completely differently, in fact, with
no chains (or total order) and no blocks, and without any theoretical limitations
on the number of transactions per second. FastSet brings a general mechanism
to settle any claims that are verifiably true, including universal programmabil-
ity: any computations done using any programming models, languages, or VMs.
At a minimum, FastSet is to FastPay what Ethereum is to Bitcoin.
FastSet is orthogonal to and can be integrated with various execution lay-
ers. These execution layers become FastSet clients/accounts which generate
verifiable computational claims, whose proofs will be broadcast to the relevant
verifiers for verification. Once verified, these computational claims are settled
by the validators, and the other clients, such as L1s, L2s, bridges, games, and
so on, can use them to verifiably proceed to their next states. Fast execution
technologies such as MegaETH (https://megaeth.com) and Groundhog [19]
will be fast computational claims generators on FastSet. These efficient execu-
tion layers also exploit commutativity, as FastSet does, but in a different setting
where transactions must eventually be linearly ordered and processed.
FastSet can be extended or optimized in several directions. In the current
design, FastSet validators use and maintain their own local state and need not
communicate with each other. Consistency is maintained thanks to weak in-
dependence of claims and commutativity of claim settlement. However, broad-
casting to a large number of validators may not be practical in some situations,
so gossip communication among the validators can be allowed to more effi-
ciently broadcast new claims across the network, using related technologies in
coding theory such as Optimum [5] (https://getoptimum.xyz) and set recon-
ciliation [23]. We currently assume that all validators are involved in all claim
validations, which means that for any claim settlement, a quorum of the entire
base of validators must be reached. Adding more validators to the network, in
54
theory, will not increase the latency of FastSet because validators work indepen-
dently from each other. However, it will increase the network traffic. A possible
solution is to adopt a committee-based approach like the one in Algorand [7] to
enable even more parallel settlement and thus further improve performance. In
the current design, a certified but (temporarily) invalid claim is marked as pre-
settled and then queued in Step 6. In the worst and most unfortunate scenario,
the queue can grow indefinitely. For example, a possibly malicious application
may DOS attack some validators by delaying them from seeing messages which
would turn the currently-invalid certified claim valid. There exist many possible
solutions. A time-based solution relying on the timestamping feature introduced
in Section 4.14 may allow validators to discard invalid certified claims that have
become outdated and thus allow FastSet to offer a perfect-past consensus re-
sult like in POD [2]. Another possible solution is to enable the processing and
settlement of invalid claims, generalizing FastPay [3], where invalid payments
can still be processed and settled, resulting in (temporary) negative account
balances. We leave these extensions and optimizations as future work.
FastSet should not be regarded as the foundation for the next blockchain.
It should be regarded as the Web3 infrastructure on which the next wave of
blockchains and verifiable computing applications will be built.
Acknowledgements. The motivation and inspiration for this work came from
herculean efforts made by the Pi Squared team to retrofit their (zero-knowledge)
Proof of (mathematical) Proof technology to blockchains. Proof of Proof (π 2 )
yields optimal verifiable computing for any programming language, by gener-
ating the cryptographic proofs from mathematical proofs produced using an
executable formal semantics. Unlike in the existing zkVM approaches, π 2 re-
quires no translation or compilation to a specific VM or ISA, and it also allows
well-understood mathematical proof engineering to be done before any cryptog-
raphy. There was no clean way to bring the full benefits of π 2 to Web3 through
blockchains, because these come hardwired with particular VMs and unneces-
sarily enforce sequencing, which is often considered subjective and controversial
(MEV). Indeed, π 2 is truly universal in the PL/VM and sequencing can and
should be specified by the apps that need it (see Section 4.13), and not enforced
on the entire universe as an artifact of an old belief that it is required in or-
der to avoid double-spending. Our warmest thanks to the Pi Squared team,
whose efforts to avoid the blockchain limitations above have lead to the design
of FastSet as a new Web3 infra. Specifically, we’d like to thank Brandon Moore,
Musab Alturki, Traian-Florin Serbanuta, Virgil Serbanuta, Dorel Lucanu, Iulia
Bastys, and Ovidiu Damian for suggestions on how to improve this paper.
References
[1] Gul A. Agha. Actors: A Model of Concurrent Computation in Distributed
Systems. MIT Press, Cambridge, MA, USA, 1986.
55
[2] Orestis Alpos, Bernardo David, and Dionysis Zindros. Pod: An optimal-
latency, censorship-free, and accountable generalized consensus layer.
CoRR, abs/2501.14931, 2025.
[3] Mathieu Baudet, George Danezis, and Alberto Sonnino. FastPay: High-
performance byzantine fault tolerant settlement. In AFT ’20: 2nd ACM
Conference on Advances in Financial Technologies, New York, NY, USA,
October 21-23, 2020, pages 163–177. ACM, 2020.
[4] Peter Burmeister. Partial algebras—survey of a unifying approach to-
wards a two-valued model theory for partial algebras. Algebra Universalis,
15(1):306–358, 1982.
[5] Supratim Deb, Muriel Médard, and Clifford Choute. Algebraic gossip:
a network coding approach to optimal multiple rumor mongering. IEEE
Transactions on Information Theory, 52(6):2486–2507, 2006.
[6] FIDE. Fide laws of chess, 2023. Accessed: 2025-06-02. Section E: Algebraic
Notation outlines the standard for recording chess moves in international
competitions.
[7] Yossi Gilad, Rotem Hemo, Silvio Micali, Georgios Vlachos, and Nickolai
Zeldovich. Algorand: Scaling byzantine agreements for cryptocurrencies.
In Proceedings of the 26th Symposium on Operating Systems Principles,
Shanghai, China, October 28-31, 2017, pages 51–68. ACM, 2017.
[8] Rachid Guerraoui, Petr Kuznetsov, Matteo Monti, Matej Pavlovic, and
Dragos-Adrian Seredinschi. The consensus number of a cryptocurrency
(extended version), 2019.
[9] Rachid Guerraoui, Petr Kuznetsov, Matteo Monti, Matej Pavlovic, and
Dragos-Adrian Seredinschi. The consensus number of a cryptocurrency.
Distributed Computing, 35(1):1–15, 2022.
[10] Maurice Herlihy and Jeannette M. Wing. Linearizability: A correctness
condition for concurrent objects. ACM Transactions on Programming Lan-
guages and Systems, 12(3):463–492, 1990.
[11] Carl Hewitt, Peter Boehler Bishop, and Richard Steiger. A universal mod-
ular ACTOR formalism for artificial intelligence. In Proceedings of the 3rd
International Joint Conference on Artificial Intelligence. Standford, CA,
USA, August 20-23, 1973, pages 235–245. William Kaufmann, 1973.
[12] Leslie Lamport. Time, clocks, and the ordering of events in a distributed
system. Communications of the ACM, 21(7):558–565, July 1978.
[13] Leslie Lamport. The part-time parliament. Technical Report SRC Report
49, Digital Equipment Corporation Systems Research Center, Palo Alto,
CA, September 1989.
56
[14] Leslie Lamport. The part-time parliament. ACM Transactions on Com-
puter Systems, 16(2):133–169, May 1998.
[15] Antoni Mazurkiewicz. Trace theory. In Wilfried Brauer, Wolfgang Reisig,
and Grzegorz Rozenberg, editors, Advances in Petri Nets 1986, Part II:
Proceedings of an Advanced Course, Bad Honnef, 8.–19. September 1986,
volume 255 of Lecture Notes in Computer Science, pages 279–324. Springer,
1987.
[16] Satoshi Nakamoto. Bitcoin: A peer-to-peer electronic cash system, Nov
2008. Accessed: 2025-06-15.
57
A Understanding SETL
A setl state includes a finite map from accounts (i.e., addresses) to the account
local states. It also includes a settled claim set that contains all the claims settled
so far. Every settled claim is naturally associated with the unique account
that submits and signs it, and this way the settled claim set can be naturally
partitioned by accounts. Claims will be processed and settled in sequences,
called blocks, causing the current setl state to change. Depending on the actual
content of the block, the state may change in various ways: a new account may
be created, a new data item may be added to an account, an existing field of an
account may be updated, some native or non-native (i.e., smart contract) assets
may be transferred from one account to another account, and so on. The settled
claim set, on the other hand, will always change in a monotonically increasing
way: it is extended by the said block.
58
– Can only submit claim blocks according to the prescribed scripts;
– Respond to messages from other accounts.
59
Via contracts and instances, setl imposes a principled scheme for various
parties and stakeholders to concurrently cooperate and coordinate in a shared
time-space. In this scheme, a contract creator sets the stage by specifying
the fields and blocks of the contract, as well as the fields and blocks of its
instances. From then on, the stage has been set. The contract account can
only behave according to the prescribed contract blocks at the request of the
contract creator, while every instance account can only behave according to the
prescribed instance block at the request of the instance creator.
A contract creation by a user-driven account effectively creates an actor.
The semantics of setl only allows the contract owner to send messages to
the contract actor. Indeed, in setl, instance fields form the state of the
contract instance actors, and these instance actors can only initiate instance
blocks according to the contract. The fields which do not have an instance
modifier form the state of the contract actor, and the blocks which do not have
an instance modifier can only be generated by the contract owner. In setl,
anybody can create instances of the contract, and each instance is an actor
controlled by the its owner user-driven account. This way, the semantics of
setl is faithful to the actor model in that actors only communicate with actors
they have knowledge about.
A clarification is needed with regards to the fact that owners of contracts
and of instances, respectively, can sign on their behalf. In Section 2, FastSet
stipulates that each account a can only submit claim blocks which are signed
by a. In setl, on the other hand, each contract creation and each instance of
it becomes an actor, which has its own state and address. However, it would be
rather inconvenient and likely insecure and unmanageable by users to allow each
actor to own its own private key to sign all its claim blocks. Instead, setl allows
the contract-based actors to only be controled by their owners, technically just
other accounts that have a private key and thus can sign messages. That is, the
unique owner of the created contract, or the unique owner of some instance of
it, is the only entity that can sign blocks on behalf of the address corresponding
to the associated contract or instance actor. This is not a modification of the
FastSet protocol, but rather a particular implementation of it. Indeed, a single
private key can be used to generate infinitely many public keys through various
methods, like hierarchical deterministic (HD) wallets or different cryptographic
algorithms. These different public keys are deterministically derived from the
same private key and are used as contract-based actor addresses in setl, al-
though there is only one initial pairing between a private key and its primary
public key.
60
so on, which may be instantiated at account’s creation time. To avoid code
duplication and save validator state space, setl allows templates.
Templates are data that can be encoded in the account messages and stored
in the account states. They will be processed by the FastSet validators, possibly
with concrete parameters and arguments, to produce actual scripts and claim
blocks, at account creation time or at block generation time.
Templates can be for anything, from simple facts and statements, to books,
pictures, videos, programming language specifications, and programs in any
programming languages. FastSet gives them no intrinsic meaning except for
their parameters, which can be instantiated. Applications and/or potential
FastSet commands are free to give them any meaning, or no meaning at all. For
example, setl will shortly introduce a claim construct called contract, which
will create a contract from a template.
Templates are related to macros and, more broadly, to meta-programming.
Macros and meta-programming languages are, however, notoriously difficult to
design. Numerous questions have to be answered. What parameters are al-
lowed to appear in a template? Should templates have types? When is the
well-formedness and well-typedness of a template checked, statically or dynami-
cally? Are nested templates allowed? How and when is a template encoded and
decoded? Since setl is meant to be a scripting language, its template support
is minimal—introduced by means of examples in Sections 4.3.2 and 4.3.3. More
advanced and meaningful code compositionality mechanisms can be deployed
separately on top of this minimal infrastructure, similarly to how programming
languages (Section 4.6) and programs (Section 4.7) are settled.
61
the authority currently collects 999 votes. It is still possible and valid to process
Alice’s vote, or Bob’s vote, but not both of them.
We have decided to give clients and applications the freedom to adventure
beyond the safe territory of weak independence. Some applications, such as
dynamic multi-sig or voting, can choose to do so, knowing that by breaking the
weak independence assumption their applications and accounts may get stuck,
in which case FastSet implementations may provide means for them to unstuck
themselves by clearing the different versions of pending claims among all the
FastSet validators. Other applications which want to stay in the safe domain
of weak independence should be incentivised to use static analysis or formal
verification techniques to prove that it is indeed the case. FastSet/setl will
also be able to provide a set of guidelines that help to write applications that
respect the weak independence assumption.
Given that said, we do expect that situations where the weak independence
assumption must be compromised, to be quite rare. The majority examples
presented in the subsequent sub-sections, especially verifiable computing in
Section 4.8, escrow in Section 4.11, and non-native assets and payments in
Section 4.12, all satisfy the assumption.
62
B Modifying Settled Data
In Section 4.5 we showed how to settle persistent data and records on FastSet, by
creating an account whose fields are initialized to the data/records that should
be settled. Since the fields cannot be modified after initialization, the weak
independence property holds.
Here we discuss possible extensions where the settled data can be modified
without compromising the weak independence property. In particular, as long
as the FastSet validators do not allow any other account (except the account
that owns the settled data) to generate claims that directly read the values of the
settled data, the weak independence property holds. The other account can still
read the values of the settled data—the FastSet validators may actually provide
SDKs for it—and generate different claims depending on the values they read.
However, they cannot generate claims whose behaviors depend on those read
values. Any read-value-sentitive decisions must be made by the clients outside
FastSet, and not by the FastSet validators.
To allow a data owner modify their settled data, we only need to define some
setter blocks. For example, if Alice wants to update her name, she can add a
new setter block:
set_name(new_name) {
verify({gov}, 1);
name := new_name;
}
The data owner can also generate conditional claims about their settled
values. Let us add a new field age to the account ALICE_INFO. Then, Alice can
claim that her name is Alice Wonderland and her age is above 21, by generating
the next claim:
We tacitly assume such boolean constructs, using standard notations (e.g., ==,
>=, and, etc), noting that FastSet implementations will likely charge clients fees
that are a function of the complexity of evaluating such boolean expressions—
since the validators will eventually have to evaluate them.
The weak independence property holds for ALICE_INFO because all the reads
(e.g., ALICE_INFO.name) and writes (e.g., set_name()) take place under one
account alice, so they are totally ordered. The other accounts can only look
at the claims that Alice settled and they must be careful with how to interpret
the claims. Indeed, the claim
only shows that once, Alice has/had such name and such age, but it does not
say that at present, Alice has such name and such age. Any attempt to achieve
the latter will break the weak independence property because it is equivalent to
allowing reads and writes of shared locations, which are not weakly independent.
63
Even if the FastSet implementation provides the capability to clients to query
other accounts’ local fields, say through an API, they should not rely on the
values of those queries. For example, it may be that Alice has submitted her
name change claim, but all validators happen to have that claim presettled (in
Step 6 in Figure 1) but not settled yet (in Step 7 in Figure 1), so the name
query made to any validator would still return the old, incorrect name, even if
made by Alice herself. Alice can settle the query herself as a claim only after
the validators have settled all her previous claims, so her query claim can be
trusted to be correct.
Other clients can also ask Alice to re-claim her name and/or age, to prevent
Alice from abusing a settled but deprecated and invalid claim. For example,
Alice may settle a claim saying that her name is “Alice Wonderland” and then
completes a name change, but the previous claim has been settled for good. In
this situation, other clients can use timestamps (explained in Section 4.14) and
reject any claims that they consider as too dated to be relevant, at the cost of
potentially breaking the weak independence property.
64