Sharding is one the key approaches to address blockchain scalability issues , and a growing number of systems are implementing sharded blockchains [1, 9, 7, 14, 10, 2]. The key idea is to create groups (or shards) of nodes that handle only a subset of all transactions and system state, relying on classical Byzantine Fault Tolerance (BFT) protocols for reaching intra-shard consensus. These systems achieve optimal performance and scalability because: (i) non-conflicting transactions can be processed in parallel by multiple shards; and (ii) the system can scale up by adding new shards. However, this separation of transaction handling across shards is not perfectly ‘clean’—a transaction might rely on data managed by multiple shards, requiring an additional step of cross-shard consensus across the concerned shards. Typically, an atomic commit protocol (such as the two-phase commit protocol ) is run across all the concerned shards to ensure that the transaction is accepted by all or none of those shards.
In this paper, we present the first replay attacks on cross-shard consensus in sharded blockchains. An attacker can launch these attacks with minimal effort, without subverting any nodes, and assuming a weakly synchronous network (and in some cases, without relying on any network assumption)—even when the byzantine safety assumptions are satisfied. These attacks compromise key system properties of safety and liveness, effectively enabling the attacker to double-spend coins (or any other objects managed by the blockchain) and create coins out of thin air. Our attacks apply to the two main approaches to achieve cross-shard consensus : (i) shard-led protocols that only involve the concerned shards, and require no external entity for coordination (Section 4); and (ii) client-led protocols that are coordinated by the client (Section 5).
We concretely sketch the replay attacks in the context of two representative systems: Chainspace  as an example of shard-led protocols; and Omniledger  as an example of client-led protocols. Not only those systems were recently presented at top security conferences, but they form the basis of numerous start-ups and open-source projects such as chainspace.io111https://chainspace.io and Harmony222https://harmony.one. However, the attacks are generic and apply to other systems that are based on similar models, like RapidChain . We also provide a comparison with mutex-based cross-shard consensus protocols used by Ethereum  in Appendix A. For each of the two cross-shard consensus approaches, Appendix B describes how an attacker can actively stage the attack by eliciting from the system the messages to replay (in contrast to passively observing the network traffic, and waiting to detect and record the target messages). We also discuss the feasibility of these attacks and their real-world impact.
Drawing insights from our analysis of performance trade-offs and replay attack vulnerabilities in existing shard-led and client-led cross-shard consensus protocols, we present a hybrid system, Byzcuit (Section 6). It combines useful features from both these design approaches to achieve high performance and scalability, and is immune to replay attacks. Byzcuit employs a Transaction Manager to coordinate cross-shard communication, reducing its cost to communication, between shards, in the absence of faults. We implement a prototype of Byzcuit in Java as a fork of the Chainspace code , and release it as an open-source project.333URL omitted for double blind review. We evaluate Byzcuit on a real cloud-based testbed under varying transaction loads and show that Byzcuit has a client-perceived latency of less than a second, even for a system load of 1000 transactions per second (tps). Byzcuit’s transaction throughput scales linearly with the number of shards by 250–300 tps for each shard added, handling up to 1550 tps with 10 shards—which is about 8 times higher than Chainspace with a similar setup. We quantify the overhead of our replay defenses and find that as expected those reduce the throughput by 20–250 tps.
We make the following key contributions: we (i) develop the first replay attacks against shard-led and client-led cross-shard consensus protocols, and illustrate their impact on important academic and implemented designs; and (ii) design a hybrid, new system Byzcuit with improved performance trade-offs, and which is resilient against the replay attacks; finally, (iii) we implement a prototype of Byzcuit and evaluate its performance and scalability on a real distributed set of nodes and under varying transaction loads, and illustrate how it is superior to previous proposals.
2 Background and Related Work
We present background and related work on cross-shard consensus protocols.
Sharded blockchains. Earlier systems like Bitcoin  probabilistically elect a single node which can extend the blockchain. However, such systems assume synchrony, have no finality (i.e., forks can exist and be eventually accepted) and low performance (i.e., high latency and low throughput). Consequently, there has been a shift to committee-based designs  where a group of nodes collectively extends the blockchain typically via classical byzantine fault tolerance (BFT) consensus protocols such as PBFT . While these systems offer better performance, single-committee consensus is not scalable—as every node handles every transaction, adding more nodes to the committee decreases throughput due to the increased communication overhead.
This motivated the design of sharded systems, where multiple committees handle a subset of all the transactions—allowing parallel execution of transactions. Every committee has its own blockchain and set of objects (or unspent transaction outputs, UTXO) that they manage. Committees run an intra-shard consensus protocol (e.g., PBFT) within themselves, and extend the blockchain in parallel.
Cross-shard consensus. In sharded systems, some transactions may operate on objects handled by different shards, effectively requiring the relevant shards to additionally run a cross-shard consensus protocol to enable agreement across the shards. Specifically, if any of the shards relevant to the transaction rejects it, all the other shards should likewise reject the transaction to ensure atomicity.
The typical choice for implementing cross-shard consensus is the two-phase atomic commit protocol . This protocol has two phases which are run by a coordinator. In the first voting phase, the nodes tentatively write changes locally, lock resources, and report their status to the coordinator. If the coordinator does not receive status message from a node (e.g., because the node crashed or the status message was lost), it assumes that the node’s local write failed and sends a rollback message to all the nodes to ensure any local changes are reversed, and locks released. If the coordinator receives status messages from all the nodes, it initiates the second commit phase and sends a commit message to all the nodes so they can permanently write the changes and unlock resources. In the context of sharded blockchains, the atomic commit protocol operates on shards (which make the local changes associated with the voting phase via an intra-shard consensus protocol like PBFT), rather than individual nodes. A further consideration is who assumes the role of the coordinator.
Related Work. Replay attacks in general have seen extensive study in the security literature. This is the first paper that presents replay attacks on cross-shard consensus protocols. Traditionally, the most stringent threat models assumed by consensus protocols involve byzantine adversaries who are able to control or subvert consensus nodes and cause them to behave arbitrarily. Repurposing those protocols to open permissionless networks (e.g., blockchains) opens up new attack avenues such as replay attacks as shown in this paper. There are currently two key approaches to cross-shard consensus . The first approach involves client-led protocols (such as Omniledger  and RSCoin ), where the client acts as a coordinator. These protocols assume that clients are incentivized to proceed to the unlock phase. While such incentives may exist in a cryptocurrency application where an unresponsive client loses its own coins if the inputs are permanently locked, these do not however hold for a general-purpose platform where transaction inputs may have shared ownership. The second approach involves shard-led protocols (such as Chainspace , Rapidchain  and Elastico ), where shards collectively assume the role of a coordinator. All the concerned shards collaboratively run the protocol between them. This is achieved by making the entire shard act as a ‘resource manager’ for the transactions it handles. We describe our replay attacks in the context of two representative systems: Chainspace  as an example of shard-led protocols (Section 4); and Omniledger  as an example of client-led protocols (Section 5). We provide a more detailed description of these systems in the relevant sections.
3 Attack Overview
Sections 4 and 5 discuss replay attacks on both shard-led and client-led cross-shard consensus protocols, respectively. We provide a high-level description of these attacks and the threat model, and describe the notation we use.
Replay Attacks on Cross-Shard Consensus. The attacker records a target shard’s responses to the atomic commit protocol, and replays them during another instance of the protocol. We present two families of replay attacks: (i) attacks against the first phase (voting), and (ii) attacks against the second phase (commit) of the atomic commit protocol.
To attack the first voting phase of the atomic commit protocol, the attacker replaces messages generated by the target shard by replaying pre-recorded messages. In practice, the attacker does not replace those messages—it achieves a similar result by making its replayed messages arrive at the coordinator faster (racing the target shard’s original message), exploiting the fact that the coordinator makes progress based on the first message it receives. Replaying messages in this fashion enables the attacker to compromise the system safety (by creating inconsistent state on the shards) and/or liveness (by causing valid transactions to be rejected).
To attack the second commit phase of the atomic commit protocol, the attacker simply replays prerecorded messages to target shards, and compromises consistency. The attacker can replay those messages at any time of its choice, and does not rely on any racing condition as in the previous case.
Threat Model. The attacker can successfully launch the described attacks without colluding with any shard nodes, and under the BFT honest majority safety assumption for nodes within shards (i.e., the attacks are effective even if all nodes are honest). We assume an attacker that can observe and record messages generated by shards. The attacker can be an external observer that passively collects the target messages at the level of the network, or it can act as a client and actively interact with the system to elicit the target messages. The attacks against the first phase of the atomic commit protocol (Sections 4.3 and 5.3) assume a weakly synchronous network in which an attacker may delay messages and race target shards by replaying pre-recorded messages. The attacks against the second phase of the atomic commit protocol (Section 4.4 and 5.4) do not make any such assumptions on the underlying network.
Notation. Operations on the blockchain are specified as transactions. A transaction defines some transformation on the blockchain state, and has input and output objects (such as UTXO entries). An object is some data managed by the blockchain, such as a bank account, a specific coin, or a hotel room. For example, represents a transaction with two inputs, managed by shard 1 and managed by shard 2; and three outputs, managed by shard 1, managed by shard 2, and managed by shard 3. We call the shards that manages the input objects input shards, and the shards that manage the output objects output shards. It is possible for a shard to be both the input and output shard. Objects can be in two states: active (on unspent) objects are available for being processed by a transaction; and inactive (or spent) objects cannot be processed by any transaction. Additionally, some systems also associate locked state with objects that are currently being processed by a transaction to protect against manipulation by other concurrent transactions involving those objects. The attacks we describe in this paper generalise to transactions with inputs and outputs managed by an arbitrary number of shards.
4 Shard-led Cross-Shard Consensus Protocol
In shard-led cross-shard consensus protocols, the shards collectively take on the role of the coordinator in the atomic commit protocol. We describe replay attacks on shard-led cross-shard consensus protocols. To make the discussion concrete, we illustrate these attacks in the context of Chainspace  (Section 4.1), though we note that these attacks can be generalized to other similar systems. We discuss how the attacker can record shard messages to replay in future attacks (Section 4.2). In Sections 4.3 and 4.4, we describe replay attacks on the first and second phase of the cross-shard consensus protocol, followed by a discussion on the real-world impact of these attacks (Section 4.5).
4.1 Chainspace Overview
Chainspace uses a shard-led cross-shard consensus protocol called S-BAC. The client submits a transaction to the input shards. Each shard internally runs a BFT protocol to tentatively decide whether to accept or abort the transaction locally, and broadcasts its local decision (pre-accept() or pre-abort()) to other relevant shards. Figure 2 shows the state machine representing the life cycle of objects in Chainspace. A shard generates pre-abort() if the transaction fails local checks (e.g., if any of the input objects are ‘inactive’ or ‘locked’). If a shard generates pre-accept(), it changes the state of the input objects to ‘locked’. This is the first step of S-BAC, and is equivalent to the voting phase in the two-phase atomic commit protocol (Section 2).
Each shard collects responses from other relevant shards, and commits the transaction if all shards respond with pre-accept(), or aborts the transaction otherwise. This is the second step of S-BAC, and is equivalent to the commit phase in the two-phase atomic commit protocol (Section 2). The shards communicate this decision to the client as well as the output shards by sending them the accept() or abort() messages. If the shard’s decision is accept(), it changes the input object state to ‘inactive’. If the shard’s decision is abort(), it changes the input object state to ‘active’ (effectively unlocking it). Upon reception of the accept(), the client concludes that the transaction was committed, and the output shard creates the output objects (with the state ‘active’) of the transaction.
Figure 1 shows an example execution of S-BAC for a valid transaction with two inputs ( and , both are active) and three outputs (), where the final decision is accept(). The client submits to shard 1 and shard 2. Upon reception of , both shard 1 and shard 2 confirm that the transaction is well-formed and the inputs objects are active, and emit pre-accept() at the end of the first phase of S-BAC. Each shard receives pre-accept() from the other shard, and emits accept() at the end of the second phase of S-BAC. As a result, the input objects and become inactive, and the output shards respectively create objects , , and .
4.2 Message Recording
Prior to the replay attacks, the attacker records responses generated by shards. The attacker can record shard responses in the first phase of S-BAC (i.e., pre-accept() or pre-abort()), enabling the family of attacks described in Section 4.3. The attacker can also record shard responses in the second phase of S-BAC (i.e., accept() or abort()), enabling the family of attacks described in Section 4.4.
In the general case, the attacker passively collects the messages either by sniffing the network on protocol executions, or by downloading the blockchain and selecting the messages to replay444Since those messages need to be recorded on chain for verification, just using transport layer encryption between nodes is not effective.. Section B.1 shows how the attacker can act as (or collude with) a client to actively elicit the messages necessary for the attacks, to record and later replay—this empowers the attacker to actively orchestrate the attacks.
4.3 Attacks on the First Phase of S-BAC
|Phase 1 of S-BAC||Phase 2 of S-BAC|
|(potential victim)||Shard 2|
|(potential victim)||Shard 1|
|(potential victim)||Shard 2|
|(potential victim)||Shard 3|
|create ; inactivate||accept()|
|create ; inactivate||-|
|create ; inactivate||abort()|
|create ; inactivate||-|
|create ; inactivate||-|
|create ; inactivate||abort()|
We present replay attacks on the first phase of S-BAC by taking the example of a transaction as described in Section 3. These attacks easily generalize to transactions with inputs and outputs managed by an arbitrary number of shards. The replay attacks work in two steps; (i) the attacker records pre-accept() or pre-abort() messages (as described in Section 4.2 and Section B.1); and (ii) then replays those messages.
Table 1 shows the replay attacks that the attacker can launch, for all possible combinations of messages emitted by shard 1 and shard 2 in the first phase of S-BAC. The caption includes details about how to interpret this table. All attacks exploit the parallel composition of multiple S-BAC instances, and insufficient binding of messages to its S-BAC instance. We describe row 6 of Table 1, to help readers interpret rest of the table on their own. In the correct execution (row 5), shard 1 and shard 2 emit pre-abort() (because is not active) and pre-accept() in the first phase, respectively. In the second phase, both shards emit abort() and the protocol terminates. Figure 3 illustrates the replay attack corresponding to row 6 of Table 1. The attacker races shard 1 by sending to shard 2 the prerecorded pre-accept() message from shard 1. As a result, shard 2 emits accept(), inactivates object and creates object . This leads to inconsistent state across the shards. In a correct execution: (i) if is accepted all its inputs ( and ) should become inactive, and all the outputs (, , ) should be created; and (ii) if is aborted, all its inputs ( and ) should become active again, and none of the outputs (, , ) should be created. However, here we have an incorrect termination of S-BAC: at the end of the protocol is active and is inactive; is not created, and are created.
Table 1 shows that through careful selection of the messages to replay from different S-BAC instances, the attacks can be effective against any shard. All the attacks (except row 4) compromise consistency; the attacker can trick the input shards to inactivate arbitrary objects, and trick the output shards into creating new objects in violation of the protocol. The attack depicted in row 4 only affects availability.
4.4 Attacks on the Second Phase of S-BAC
|Phase 2 of S-BAC|
|Shard 1||Shard 2||Shard 3 (potential victim)|
|create ; inactivate||accept()|
|create ; inactivate||-|
Table 2 shows replay attacks for all possible combinations of messages emitted by shard 1 and shard 2 in the second phase. Since the attacks we describe in this section assume that the first phase of S-BAC concluded correctly (i.e., all the relevant shards unanimously decide to accept or reject a transaction), both the shards generate abort() (row 1) or accept() (row 5). The caption includes details about how to interpret this table. We describe row 6 of Table 2, to help readers interpret rest of the table on their own. In the correct execution (row 5), both the shards emit abort() and no output objects are created. In the attack in row 6, the attacker replays a prerecorded accept() from shard 1 to all the relevant shards (in this case shard 3). Upon receiving this message, shard 3 (incorrectly) creates .
The potential victims of replay attacks corresponding to the second phase of S-BAC are the shards that only act as output shards (i.e., do not simultaneously act as input shards). The attacker can replay accept() multiple times tricking shard 3 into creating multiple times. These attacks are possible because shards do not keep records of inactive objects (following the UTXO model) for scalability reasons555Requiring shards to remember the full history of inactive objects would increase their memory requirements monotonically over time, reaching at some point memory limits preventing further operations. Thus this is a poor mitigation for the attacks presented., and because shard 3 takes part in only the second phase of S-BAC. The attacker can double-spend repeatedly by replaying a single prerecorded message multiple times, and spending the object (and effectively purging it from shard 3’s UTXO) before each replay.
Contrarily to the attacks against the first phase of S-BAC (Section 4.3), these attacks do not rely on any racing conditions; there is no need to race any honest messages.
4.5 Real-world Impact
The real-world impact and attacker incentives to conduct these attacks depends on the nature and implementation of the smart contract handling the target objects. We discuss the impact of these attacks in the context of two common smart contract applications, which are also described in the Chainspace paper . To take a concrete example, we illustrate the attack depicted in row 3 of Table 1, but similar results can be obtained with the other attacks described in Table 1 and Table 2.
One of the most common blockchain application is to manage cryptocurrency (or coins) and enable payments for processing transactions, implemented by the CSCoin smart contract in Chainspace. Lets suppose object (handled by shard 1) represents Alice’s account, and object (handled by shard 2) represents Bob’s account. To transfer coins to Bob, Alice submits a transaction , where and respectively represent the new account objects of Alice and Bob, with updated account balances. By executing the attack described in row 3 of Table 1, an attacker can trick shard 1 to abort the transaction and unlock (thus reestablishing Alice’s account balance as it was prior to the coin transfer), and shard 2 to accept the transaction and create (thus adding coins to Bob’s account). This attack effectively allows any attacker to double-spend coins on the ledger; and shows how to create coins out of thin air.
Another common blockchain use case is a platform for decision making (or electronic petitions), implemented by the SVote smart contract in Chainspace. Upon initialization, the SVote contract creates two objects: (i) representing the tally’s public key, a list of all voters’ public keys, and the tally’s signature on these; and (ii) representing a vote object at the initial stage of the election (all candidates having a score of zero) along with a zero-knowledge proof asserting the correctness of the initial stage. To vote, clients submit a transaction , where and are respectively the updated voting list (i.e., the voting list without the client’s public key), and the election stage updated with the client’s vote. By executing the attack described by row 3 of Table 1, an attacker can trick shard 1 to abort the transaction and thus not update the voting list, and shard 2 to accept the transaction and thus update the election stage. This effectively allows any client to vote multiple times during an election while remaining undetected (due to the privacy-preserving properties of the smart contract).
5 Client-led Cross-shard Consensus Protocol
We describe replay attacks on client-led cross-shard consensus protocols. We illustrate these attacks in the context of Omniledger  (Section 5.1) to make the discussion concrete. However, we note that these attacks can be generalized to other similar systems. We discuss how the attacker can record shard messages to replay in future attacks (Section 5.2). In Sections 5.3 and 5.4, we describe replay attacks on the first and second phase of the cross-shard consensus protocol. Finally, we discuss the real-world impact of these attacks (Section 5.5).
5.1 Omniledger Overview
Omniledger uses a client-led cross-shard consensus protocol called Atomix. The client submits the transaction to the input shards. Each shard runs a BFT protocol locally to decide whether to accept or reject the transaction, and communicates its response (pre-accept() or pre-abort()) to the client.666For consistency and clarity, we use the terminology used in Section 4. In Omniledger, pre-accept() is actually a proof-of-accept and pre-abort() is a proof-of-abort . A shard emits pre-abort() if the transaction fails local checks. Alternatively, if a shard emits pre-accept(), it inactivates the input objects it manages. This is the first phase of Atomix, and is similar to the voting phase in the two-phase atomic commit protocol (Section 2), but differs in that the protocol proceeds optimistically. The write changes made by the input shards in the first phase of Atomix are considered permanent (i.e., there is no ‘locked’ object state), unless the client requests the input shards to revert their changes in the second phase.
After the client has collected pre-accept() from all input shards, it submits accept() message (containing proof of the pre-accept() messages) to the output shards which create the output objects. Alternatively, if any of the input shards emits pre-abort(), the client sends abort() (containing proof of pre-abort()) to the relevant input shards which make the input objects active again. This is the second phase of Atomix, and is similar to the commit phase in the two-phase atomic commit protocol (Section 2).
Figure 4 shows execution of Atomix for a valid transaction , with two active inputs ( managed by shard 1, and managed by shard 2) and producing three outputs managed by shard 1, shard 2 and shard 3, respectively. The client sends to the input shards, both of which reply with pre-accept() and make the input objects and inactive. The client sends accept() to the output shards which respectively create objects , , and .
5.2 Message Recording
Before launching the replay attacks, the attacker first records the target shard responses. The attacker can record shard responses in the first phase of Atomix (i.e., pre-accept() or pre-abort()), enabling the attacks described in Section 5.3. The attacker can also record shard responses in the second phase of Atomix (i.e., accept() or abort()), enabling the attacks described in Section 5.4. In the general case, the attacker passively collects the messages to replay, for example by protocol executions on the network, or by downloading the blockchain and selecting the appropriate messages. Section B.2 shows how the attacker can act as (or collude with) a client to actively elicit and record the target messages to later use in the replay attacks.
5.3 Attacks on the First Phase of Atomix
|Phase 1 of Atomix||Phase 2 of Atomix|
|(potential victim)||Shard 2|
|(potential victim)||Shard 2|
|(potential victim)||Shard 3|
We present replay attacks on the first phase of Atomix by taking the example of a transaction as described in Section 3. These attacks easily generalize to transactions with inputs and outputs managed by an arbitrary number of shards. The replay attacks work in two steps: (i) the attacker observes the traffic and records pre-accept() or pre-abort() messages as described in Section 5.2; and (ii) then replay those messages.
Table 3 shows the replay attacks that the attacker can launch, for all possible combinations of responses generated by shard 1 and shard 2 in the first phase of Atomix. The caption includes details about how to interpret this table. We describe row 2 of Table 3, to help readers interpret rest of the table on their own. In the correct execution (row 1), both shard 1 and shard 2 emit pre-accept() in the first phase, and inactivate the input objects and . Upon receiving these messages, the client sends accept() to the output shards shard 1, shard 2 and shard 3, which create the output objects , and , respectively; and the protocol terminates. In the attack illustrated in row 2 of Table 3, the attacker races shard 1 by sending to the client the prerecorded pre-abort() message from shard 1. As a result, the client sends abort() message to the input shards shard 1 and shard 2, which re-activate the input objects and . This results in inconsistent state because the output objects (, , ) have been created, while the input objects (, ) are still active—in a correct execution all transaction inputs should be inactivated, and all outputs should be created.
Table 3 shows that through careful selection of the messages to replay, the attacks can be effective against any shard. The attacks illustrated in row 2, row 3, and row 4 only affect availability, while the other attacks compromise consistency (i.e., the attacker can trick the input shards to reactivate arbitrary objects, and trick the output shards into creating new objects in violation of the protocol). The potential victims of these attacks include the client (e.g., when the attacker replays the shard messages to it in the first phase of Atomix) and any input or output shards.
5.4 Attacks on the Second Phase of Atomix
|Phase 2 of Atomix|
|(potential victim)||Shard 2|
|(potential victim)||Shard 3|
We present replay attacks on the second phase of Atomix. The attacker prerecords accept() and abort() messages as described in Section 5.2.
Table 4 shows replay attacks corresponding to the messages emitted by the client in the second phase—i.e., accept() in row 1, or abort() in row 3. The caption includes details about how to interpret this table. The abort() message at (column 1, row 2) means that the attacker sends a prerecorded abort() message to the input shards (shard 1 and shard 2) impersonating the client. Upon receiving this message, shard 1 and shard 2 (incorrectly) re-activate and , respectively. Furthermore, all output shards create the output objects when the correct accept() message emitted by the client (row 1, column 1) reaches them. This results in inconsistent state, because the output objects have been created, but the input objects have not been consumed and have been reactivated by the abort() message replayed by the adversary. The potential victims of abort() replay attack are the input shards.
Similarly, accept() at (row 4, column 1) means that the attacker sends a prerecorded accept() message to the output shards (shard 1, shard 2 and shard 3) impersonating the client. Upon receiving this message, the output shards (incorrectly) create , and . Furthermore, the input shards (shard 1 and shard 2) reactivate and upon receiving the the correct abort() message emitted by the client (row 3, column 1). This creates inconsistent state: the input objects have not been consumed and have been reactivated by the abort() message emitted by the client, but the output objects have been created due to the accept() message replayed by the attacker. The potential victims of accept() replay attack are the output shards.
These attacks are possible because output shards create objects directly upon receiving accept(); they do not check if the objects have been previously invalidated because shards do not keep records of inactive objects (per the UTXO model) for scalability reasons.777Verifying that objects have not been previously invalided implies either keep a forever-growing list of invalidated objects, or download and check the shard’s entire blockchain. The attacker can double-spend the output objects repeatedly from a single prerecorded message by replaying it multiple times, and spending the object (and effectively purging it from the output shards’ UTXO) before each replay.
Similar to the attacks against the second phase of S-BAC (Section 4.4), these attacks do not exploit any racing condition and can be mounted by an adversary at a leisurely pace.
5.5 Real-world Impact
Contrarily to Chainspace, Omniledger does not support smart contracts and only handles a cryptocurrency. The attacks described in Sections 5.3 and 5.4 allow an attacker to: (i) double-spend the coins of any user, by reactivating spent coins (e.g., the attacker may execute the attack depicted by row 2 of Table 4 to re-activate the objects and after the transfer is complete); and (ii) create coins out of thin air by replaying the message to create coins (e.g., an attacker may execute the attack depicted by row 4 of Table 4 to create multiple times object , by purging it from the UTXO list of shard 3 prior to each instance of the attack). If the attacker colludes with the client, it can trigger the prerecorded messages needed for the attacks as described in Section 5.2. Alternatively, the attacker can passively observe the network and collect the target messages to replay. Similar results can be obtained using the attacks described in Table 3.
Note that since transaction are recorded on the blockchain, these attacks can be detected retrospectively. This can lead to the attacker being exposed, or the attacker can inculpate innocent users (the attacker can replay messages of any user).
6 The Byzcuit Atomic Commit Protocol
We previously discussed the two main approaches to achieve cross-shard consensus in sharded blockchains: shard-led protocols in the context of S-BAC (Section 4.1), and client-led protocols in the context of Atomix (Section 5.1). S-BAC runs the protocol among the shards, without relying on client coordination. But this comes at the cost of increased cross-shard communication: all input shards communicate with all other input shards, which leads to communication complexity of where is the number of input shards.
On the other hand, Atomix is a simpler protocol, and using the client to coordinate cross-shard communication can reduce the cost to in the number of shards (by aggregating shard messages). However, an unresponsive or malicious client can permanently lock input objects by never initiating the second phase of the protocol, requiring additional design considerations (e.g., a new entity that periodically unlocks input objects for transactions on which no progress has been made). Moreover, we have highlighted that both shard-led (Sections 4.3 and 4.4) and client-led (Sections 5.3 and 5.4) protocols are vulnerable to replay attacks that can compromise system liveness and safety.
Motivated by these insights, we present Byzcuit—a cross-shard atomic commit protocol that is based on S-BAC, and integrates design features from Atomix. Byzcuit allocates a Transaction Manager (TM) to coordinate cross-shard communication, reducing its cost to in the happy case888The communication complexity can be reduced to in the number of shards by aggregating shard messages as described by Omniledger.; alternatively Byzcuit also has a fall-back mode in case the TM fails, similar to Atomix and traditional two phase commit protocols. Byzcuit achieves resilience against the replays attacks described in Section 4 and Section 5, by leveraging the notion of dummy objects and object sequence numbers, which have been explained in the following subsections.
6.1 Byzcuit Protocol Design
First, the input shards do not have a way to know that particular protocol messages received correspond to a specific instance (or session) of a transaction. This gap in the input shards’ knowledge enables an attacker to replay, mix and match, old messages leading to attacks. To address this limitation, we associate a session identifier with each transaction, which has to be crafted carefully to not degrade the performance of the protocols significantly—such as, for example, by requiring nodes to store state linearly in the number of past transactions.
The second reason that facilitates the replay attacks is that in some cases the output shards are only involved in the second phase of the protocol, and therefore have no knowledge of the transaction context (to determine freshness) that is available to the input shards. We address this limitation by introducing the notion of dummy objects: each shard creates a fixed number of dummy objects upon configuration; if a shard only serves as an output shard for a transaction (and therefore will only be involved in the second phase of the protocol), Byzcuit forces it to be involved in the first phase of the protocol by implicitly including a dummy object managed by the output shard in the transaction inputs, which will create a new dummy object upon completion. As a result, the output shard also becomes an input shard (because of the inclusion of its dummy object in the transaction inputs) and witnesses the entire protocol execution, rather than just the second phase.
Byzcuit Protocol Execution.
We illustrate Byzcuit taking the example of a transaction with two input objects, managed by shard 1 and managed by shard 2; and three outputs, managed by shard 1, managed by shard 2, and managed by shard 3.
Figure 5 illustrates the Byzcuit protocol; the client first sends the transaction to all input and output shards. Note that this is different than other protocols like S-BAC and Atomix, where the transaction is only sent to the input shards. As mentioned previously, to achieve resilience against replay attacks, Byzcuit forces a shard that is only involved in creating the output objects to also become an input shard (and witness the transnational context by participating in the first phase of the protocol) by implicitly consuming one of its dummy inputs (which creates a new dummy object upon completion). Byzcuit associates a sequence number to each object and dummy object (when the object is created ). The sequence number is intrinsically linked to the object: when clients query shards to obtain an object , they also receive the associated sequence number .
When submitting the transaction , the client also sends along a transaction sequence number , where the transaction sequence number is the maximum of the sequence numbers of each input object and dummy objects (➊).
Upon receiving a new pair , each shard saves in a local cache memory—the transaction sequence number acts as session identifier associated with the transaction . Each shard internally verifies that the transaction passes local checks, and that is equal to (or bigger than) the sequence numbers of the objects they manage (i.e., shard 1 checks , shard 2 checks , shard 3 checks ). The shards send their local decision to the TM: pre-accept() for local accept (and the shard locks the objects it manages), or pre-abort() for local abort.
After receiving all the messages corresponding to the first phase of Byzcuit from the concerned shards, the TM sends a suitable message to the shards (accept() if all the shards respond with pre-accept(), or abort() otherwise). Upon receiving accept() or abort() from the TM, shards first verify that they previously cached the pair associated with the message; otherwise they ignore it (➋).
The accept() or abort() messages sent by the TM provide enough evidence to the shards to verify whether is correctly computed; i.e. shards verify that is at least the maximum of the sequence numbers of each input and dummy object by inspecting the transaction signed by each shard. If accept() has a correct , the shards inactivate the input objects and create the output objects , and shard 3 creates a new dummy object ; otherwise, they update the sequence numbers of each input object and dummy object to , i.e. shards locally update and , and . Shards delete from their local cache (➌).
Since we assume that shards are honest—inline with the threat model of the systems discussed—it suffices if only one shard notifies the client of the protocol outcome; we may set any arbitrary rule to decide which shard notifies the client (e.g., the shard handling the first input object) (➍).
Figure 6 shows the finite state machine describing the life cycle of Byzcuit objects.
The Transaction Manager (TM) coordinates cross-shard communication in Byzcuit. We now discuss who might play the role of the TM, and argue that Byzcuit guarantees liveness even if the TM is faulty (byzantine) or crashes.
Keeping with the overall design goal of decentralization, we envision that a designated shard will act as the TM. If the shard is honest, the TM is live—and therefore progress is always made. The input shards contact in turn each node of the TM shard until they reach one honest node. The TM shard may have up to dishonest nodes; therefore, the client or the input shards need to send messages to at least nodes of the TM shard to ensure that it is received by at least one honest node999Clients may take a statistical view of availability. Given that fewer than of nodes in a shard are dishonest, sending the transaction to nodes will fail to reach an honest node with probability only
nodes will fail to reach an honest node with probability only. Clients may also send messages sequentially to nodes, and only continue if they do not observe progress within some timeout to further reduce costs.. Thus, as soon as the first honest node receives the message, the protocol progresses.
If the TM is the client or any centralized party, it may act arbitrarily—but this does not stall the protocol because anyone can make the protocol progress by taking over at any time the role of the TM. This is possible because the TM does not act on the basis of any secrets, therefore anyone else can take over and complete the protocols. This “anyone” may be an honest node in a shard that wants to finally unlock an object (e.g., upon a timeout); or other clients that wish to use a locked object; or it may be an external service that has a job to periodically close open Byzcuit instances. Byzcuit ensures such parties may attempt to make progress asynchronously and concurrently safely. Therefore, Byzcuit guarantees liveness as long as there is at least one honest entity in the system.
Handling Sequence Number Overflow.
An attacker can try to exhaust the possible sequence numbers to make them overflow. The attacker submits a pair such that the sequence number is just below the system overflow value; the sequence numbers associate with the inputs overflow upon the next updates, and the system would be again prone to the attacks described in Section 4.3101010Note that this overflow vulnerability is common to every system relying on nonces chosen by the users, like Byzantine Quorum Systems .. To mitigate this issue, shards define a clone procedure allowing to update any of their objects to an unchanged version of themselves (i.e. it creates a fresh copy of the object). This clone procedure effectively creates a new object with serial number . When shards detect that the serial number of one of their objects approaches the overflow value, they execute internally this clone procedure. The attacker may exploit this mechanism to DoS the system, forcing shards to constantly update their objects; as a result, the target objects are not available to users. DoS countermeasures are out of scope, and are typically addressed by introducing transaction fees.
6.2 Security against Replay Attacks
We argue that Byzcuit is resilient to replay attacks. We recall the Honest Shard assumption from Chainspace and Omniledger under which Byzcuit operates, and assume that messages are authenticated as in traditional BFT protocols.
Security Assumption 1.
(Honest Shard ) The adversary may create arbitrary smart contracts, and input arbitrary transactions into Byzcuit, however they are bound to only control up to faulty nodes in any shard. As a result, and to ensure the correctness and liveness properties of Byzantine consensus, each shard must have a size of at least nodes. (From Chainspace .)
Any message emitted by shards comes with at least signatures from nodes. Assuming honest shards, the attacker can forge at most signatures, which is not enough to impersonate a shard.
Security of the first phase of Byzcuit.
An attacker may try to replay pre-accept() and pre-abort() during the first phase of the protocol, similarly to the attacks described in Sections 4.3 and 5.3; the TM then aggregates these messages into either accept() or abort(), and forwards them to the shards during the second phase of the protocol. Theorem 1 shows that Byzcuit detects that they originate from replayed messages and ignores them.
Under Honest Shard assumption, Byzcuit ignores accept() and abort() messages issued from replayed pre-accept() and pre-abort().
A proof of Theorem 1 can be found in Section C.1. Intuitively, the transaction sequence number acts as a monotonically increasing session identifier associated with the transaction ; the attacker cannot obtain prerecorded messages containing a fresh . Byzcuit shards can then distinguish replayed messages (i.e., messages with old ) from the messages coming from the instance of the protocol that they are executing (i.e., messages with fresh ).
Security of the second phase of Byzcuit.
An attacker may try to replay accept() and abort() messages during the second phase of the protocol, similarly to the attacks described in Sections 4.4 and 5.4; Theorem 2 shows that Byzcuit ignores those replayed messages.
Under Honest Shard assumption, Byzcuit ignores replayed accept() and abort() messages.
A proof of Theorem 2 can be found in Section C.2. Intuitively, these attacks target shards acting only as output shards (and not also as input shards) and exploit the fact that they are only involved in the second phase of the protocol, and therefore have no knowledge of the transaction context (to determine freshness) that is available to the input shards. Byzcuit is resilient to these replay attacks as it is designed in such a way that there are no shards that act only as output shards; all output shards are forced to also become input shards, by introducing dummy objects if they do not manage any input objects; this prevents the attacks as the attack targets no longer exist.
7 Implementation & Evaluation
We implement a prototype of Byzcuit (Section 6) in Java and evaluate its performance and scalability. To analyze the overhead introduced by our replay attack defenses (i.e., with message sequence numbers and dummy objects), we compare Byzcuit with replay defenses (byzcuit) with the baseline of Byzcuit without any replay attack defenses (byzcuit-baseline).
Our implementation of Byzcuit is a fork of the Chainspace code , and is released as an open-source project.111111https://github.com/sheharbano/byzcuit For BFT consensus, we use the bft-SMaRt  Java library (based on PBFT ), which is one of the very few maintained open source BFT libraries. End users run a client to communicate with Byzcuit nodes, which sends transactions according to the bft-SMaRt protocol. The Byzcuit client also acts as the Transaction Manager (TM) and is responsible for driving the cross-shard consensus.
We evaluate the performance and scalability of our Byzcuit implementation through deployments on Amazon EC2 containers. We launch up to 96 instances for shard nodes and 96 instances for clients on t2.medium virtual machines, each containing 8 GB of RAM on 2 virtual CPUs and running GNU/Linux Debian 8.1. We use 4 nodes per shard. Each measured data point corresponds to 10 runs represented by error bars. The error bars in Figure 7 and Figure 8
show the average and standard deviation, and the error bars inFigure 9 show the median and the 75th and 25th percentiles.
Throughput and Scalability. Figure 7 shows the throughput of Byzcuit (the number of transactions processed per second, tps) corresponding to an increasing number of shards. Each transaction has 2 input objects and 5 output objects, chosen randomly from shards. We test transactions with 5 output objects for a fair evaluation of Byzcuit’s replay defenses by triggering the creation of dummy objects (i.e., a large number of output objects and a small number of input objects implies a higher probability of output-only shards getting selected, triggering the creation of dummy objects). We find that byzcuit has a throughput of 260 tps for 2 shards, and linearly scales with the addition of more shards achieving up to 1550 tps for 10 shards. As expected, the throughput of byzcuit is lower than byzcuit-baseline by a somewhat constant factor ranging from 20–200 tps, but still increases linearly. This is expected because the creation of dummy objects in byzcuit leads to a higher number of shards processing the same transaction compared to byzcuit-baseline, leading to lower concurrency and lower throughput.
Another interesting observation is that the design and implementation optimizations in byzcuit lead to significantly higher throughput than Chainspace, even though the former has lower concurrency due to the dummy objects. For similar experimental setup and for 2–10 shards, Chainspace achieves 70–180 tps, while byzcuit achieves 260–1550 tps. This is due to the improved design of the cross-shard consensus protocol (Section 6), which results in communication complexity of in contrast to Chainspace’s (where is the number of input shards). Another reason for byzcuit’s significant throughput improvement is that unlike Chainspace, all interactions between the Transaction Manager and the shards are asynchronous. This eliminates the blocking condition in Chainspace where a shard cannot commit a transaction in the second phase of the cross-shard consensus protocol, until it receives messages from all concerned shards corresponding to the first phase.
The Effect of Dummy Objects on Throughput. We previously observed that the throughput of byzcuit is lower than byzcuit-baseline due to the creation of dummy objects to mitigate replay attacks. Figure 8 shows the extent of throughput degradation due to dummy objects. We submit specially crafted transactions to 6 shards, such that each transaction has 1 input object, and we vary the number of dummy objects from 1–5 selected from unique shards, resulting in a corresponding decrease in concurrency because as many shards end up processing the transaction. For example, 2 dummy objects means that 3 shards process the transaction (1 input shard, and 2 more shards corresponding to the dummy objects). As expected, the throughput decreases by 20–250 tps with the addition of each dummy object, and reaches 750 tps when all 6 shards handle all transactions.
Client-perceived Latency. Figure 9 shows the client-perceived latency—the time from when a client submits a transaction, until it receives a decision from Byzcuit about whether the transaction has been committed—under varying system loads (expressed as transactions submitted to Byzcuit per second). We submit a total of 1200 transactions at 200–1000 transactions per second to Byzcuit with 6 shards. Each transaction has 2 inputs objects and 5 output objects, both chosen randomly from shards. When the system is experiencing a load of up to 1000 tps, clients hear back about their transactions in less than a second on average, even with our replay attack defenses.
We presented the first replay attacks against cross-shard consensus protocols in sharded distributed ledgers. These attacks affect both shard-driven and client-driven consensus protocols, and allow attackers to double-spend or lock objects with minimal efforts. The attacker can act independently without colluding with any nodes, and succeed even if all nodes are honest; most of the attacks work also under asynchrony. While addressing these attacks seems like an implementation detail, their many variants illustrate that a fundamental re-think of cross-shard commit protocols is required to protect against them.
We developed Byzcuit, a new cross-shard consensus protocol merging features from shard-led and client-led consensus protocols, and withstanding replay attacks. Byzcuit can be seen as unifying Atomix (from Omniledger) and S-BAC (from Chainspace), into an protocol, that is efficient and secure. We implemented a prototype of Byzcuit and evaluated it on a real cloud-based testbed, showing that it is more performant than Chainspace, and on par with Omniledger performance. The resulting protocol is a drop-in replacement for either, and can be adopted to immunize systems based on those designs.
George Danezis, Shehar Bano and Alberto Sonnino are supported in part by EPSRC Grant EP/N028104/1 and the EU H2020 DECODE project under grant agreement number 732546 as well as chainspace.io. Mustafa Al-Bassam is supported by The Alan Turing Institute. We thank Eleftherios Kokoris-Kogias for helpful suggestions on early manuscripts.
-  Al-Bassam, M., Sonnino, A., Bano, S., Hrycyszyn, D., and Danezis, G. Chainspace: A Sharded Smart Contracts Platform. In The Network and Distributed System Security Symposium (2018).
-  Bano, S., Sonnino, A., Al-Bassam, M., Azouvi, S., McCorry, P., Meiklejohn, S., and Danezis, G. SoK: Consensus in the Age of Blockchains. https://arxiv.org/abs/1711.03936, 2017.
-  Bessani, A., Sousa, J. a., and Alchieri, E. E. P. State Machine Replication for the Masses with BFT-SMART. In IEEE/IFIP International Conference on Dependable Systems and Networks (2014).
-  Buterin, V. Cross-shard contract yanking. https://ethresear.ch/t/cross-shard-contract-yanking/1450, 2018.
-  Castro, M., Liskov, B., et al. Practical Byzantine Fault Tolerance. In Symposium on Operating Systems Design and Implementation (1999), vol. 99, pp. 173–186.
-  Castro, M., Liskov, B., et al. Practical Byzantine fault tolerance. In OSDI (1999), vol. 99, pp. 173–186.
-  Danezis, G., and Meiklejohn, S. Centrally Banked Cryptocurrencies. In The Network and Distributed System Security (2016).
-  Gray, J. N. Notes on Database Operating Systems. In Springer Operating Systems. 1978, pp. 393–481.
-  Kokoris-Kogias, E., Jovanovic, P., Gasser, L., Gailly, N., Syta, E., and Ford, B. OmniLedger: A Secure, Scale-Out, Decentralized Ledger via Sharding. In IEEE Symposium on Security and Privacy (2018).
-  Luu, L., Narayanan, V., Zheng, C., Baweja, K., Gilbert, S., and Saxena, P. A Secure Sharding Protocol For Open Blockchains. In ACM SIGSAC Conference on Computer and Communications Security (2016), pp. 17–30.
-  Malkhi, D., and Reiter, M. Byzantine Quorum Systems. Distributed computing 11, 4 (1998), 203–213.
-  Nakamoto, S. Bitcoin: A Peer-to-Peer Electronic Cash System. https://bitcoin.org/bitcoin.pdf, 2008.
-  Wood, G. Ethereum: A Secure Decentralised Generalised Transaction Ledger EIP-150 Revision. http://gavwood.com/paper.pdf, 2016.
-  Zamani, M., Movahedi, M., and Raykova, M. RapidChain: Scaling Blockchain via Full Sharding. In ACM SIGSAC Conference on Computer and Communications Security (2018), pp. 931–948.
Appendix A Comparison with Mutex-based Cross-shard Consensus Protocols
Mutex-based schemes for cross-shard transactions, such as Ethereum’s cross-shard “yanking” proposal , find a way to avoid complex cross-shard coordination for transactions that involve objects managed by different shards. The key idea is to require all objects that a transaction reads or writes to be in the same shard (i.e., all locks for a transaction are local to the shard). Cross-shard transactions are enabled by transferring the concerned objects between shards, effectively giving shards a lock on those objects. When shard 1 transfers an object to shard 2, shard 1 includes a transfer “receipt” in its blockchain. A client can then send to shard 2 a Merkle proof of this receipt being included in shard 1’s blockchain, which makes the object active in shard 2.
Mutex-based schemes also need to consider replay attacks. Clients can claim the same receipt multiple times, unless shards store information about previously claimed receipts. Naïvely, shards have to store information about all previously claimed receipts permanently. However, two intermediate options with trade-offs have been proposed :
Shards only store information about receipts for blocks; so clients can only claim receipts within blocks, and objects are permanently lost if not claimed within blocks.
Shards only store information about receipts for blocks, and include the root of a Merkle tree of claimed receipts in their blockchain every blocks. If a receipt is not claimed within blocks, the client must provide one Merkle proof every blocks that have passed to show that the receipt has not been previously claimed, in order to claim it. The longer the receipt was not claimed, the greater the number of proofs that are needed to claim a receipt. These proofs need to be also stored on-chain to allow other nodes to validate them.
Byzcuit forgoes the need for shards to store information about old state (such as inactive objects or old receipts) as shards only need to know the set of active objects they manage, and does not impose a trade-off between the amount of information about old state that needs to be stored and the cost of recovering old state that was held up in an incomplete cross-shard transaction (i.e., an unclaimed receipt).
Appendix B Eliciting Messages to Replay
We show how the attacker can act as (or collude with) a client to actively elicit and record the target messages to later use in the replay attacks. This empowers the attacker to actively orchestrate the attacks. We describe how the attacker can trigger target messages in the context of an example, without loss of generality. Lets assume that shard 1 manages objects (‘active’) and object (‘inactive’ or non-existent), and shard 2 manages object (‘active’); means any inactive object on the shard, and means any output object (i.e., their details do not matter).
b.1 Shard-led Cross-Shard Consensus
We show how the attacker can act as (or collude with) a client to actively elicit and record the target messages, in the context of shard-led cross-shard consensus protocols as illustrated by Section 4. To elicit pre-accept() for a transaction (the output is not relevant here) from shard 1, the key consideration is to closely precede the transaction with another transaction that: (i) locks the inputs managed by at least one other shard (in this case on shard 2); and (ii) to ensure that the preceding transaction gets ultimately aborted, and becomes active again. The steps look as follows:
The attacker submits to shard 2. This locks .
The attacker quickly follows up by submitting to shard 1 and shard 2. Shard 1 generates pre-accept(), which is the target message that the attacker records. Shard 2 generates pre-abort() because is locked by . Consequently, in the second phase of S-BAC, both shard 1 and shard 2 end up aborting .
is eventually aborted, making active again.
To elicit pre-abort() for a transaction (the output is not relevant here) from shard 1, the key consideration is to closely precede the transaction with another transaction that locks the input managed by the shard (in this case on shard 1). The steps look as follows:
The attacker submits to shard 1. This locks .
The attacker quickly follows up by submitting to shard 1 and shard 2. Shard 1 generates pre-abort() because is locked by , which is the target message that the attacker records. Shard 2 generates pre-accept(). Consequently, in the second phase of S-BAC, both shard 1 and shard 2 end up aborting .
is eventually aborted, making active again.
b.2 Client-led Cross-Shard Consensus
We show how the attacker can act as (or collude with) a client to actively elicit and record the target messages, in the context of client-led cross-shard consensus protocols as illustrated by Section 5. s To elicit pre-accept() from shard 1 for a transaction (the output is not relevant here) from shard 1, the key consideration is to closely precede the transaction with another transaction that: (i) temporarily spends the inputs managed by at least one other shard (in this case on shard 2); and (ii) to ensure that the preceding transaction is ultimately aborted so that becomes active again. The steps look as follows:
The attacker submits to shard 2, where is managed by a different shard. Shard 2 emits pre-accept() and marks as inactive.
The attacker follows up by submitting to shard 1 and shard 2. Shard 1 generates pre-accept(), which is the target message that the attacker records. Shard 2 generates pre-abort() because is inactive.
The attacker submits abort() to shard 1 to reactivate , and sends abort() to shard 2 to reactivate .
For the attacks described in Section 5.4, the attacker needs to elicit abort() and accept() from the target shards. For the former, the attacker can follow the steps described previously to elicit pre-accept() and pre-abort(). To elicit accept(), the attacker simply submits transaction and observes and records its successful execution.
Under Honest Shard assumption, no attacker can obtain prerecorded messages containing a fresh transaction sequence number .
The core idea protecting Byzcuit from these replay attacks is that the attacker can only obtain prerecorded messages associated with old transaction sequence numbers . The transaction sequence number is fresh only if it is at least equal the maximum of the sequence number of all input and dummy objects of the transaction . Shards update every input and dummy object sequence number upon aborting transactions in such a way that sequence numbers only increase. That is, after emitting pre-accept() or pre-abort(), either the sequence number of all input and dummy objects of are updated to a value bigger than (in case of pre-abort()), or the objects are inactivated which prevents any successive transaction to use them as input (in case of pre-accept()). It is therefore impossible for the adversary to hold a prerecorded message for a fresh since the only prerecorded messages that the adversary can obtain contain sequence numbers smaller than . ∎
c.1 Proof of Theorem 1
Under Honest Shard assumption, Byzcuit ignores accept() and abort() messages issued from replayed pre-accept() and pre-abort().
Figure 6 shows that once Byzcuit locks objects for a particular pair , the protocol can only progress toward accept() or abort(); i.e. shards can either accept or abort the transaction . The attacker aims to trick one or more shards to incorrectly accept or abort by injecting prerecorded messages during the first phase of Byzcuit; we show that the attacker fails in every possible scenario.
Suppose transaction should abort (the TM outputs abort()), but the attacker tries to trick some shards to accept the transaction. Figure 6 shows that the attacker can only succeed the attack if they gather accept() containing a fresh transaction sequence number . Lemma 1 states that no attacker can obtain prerecorded messages over fresh transaction sequence number ; therefore the only messages available to the adversary at this point of the protocol are (at most) pre-accept() and (at most) abort(), where is the number of concerned shards. This is not enough to form an accept() message with a fresh transaction sequence number (which is composed of pre-accept()); therefore the attacker cannot trick any shard to accept the transaction.
Suppose transaction should be accepted (the TM outputs accept() with a fresh ), but the attacker tries to trick some shards to abort the transaction. Figure 6 show that Byzcuit does not require a fresh transaction sequence number to abort transactions (the freshness of is only enforced upon accepting a transaction); but shards locked the input and dummy objects of the transaction for the pair (with fresh ), so the attacker needs to gather abort() containing the same transaction sequence number locked by shards. Lemma 1 shows that the attacker cannot obtain prerecorded messages over fresh ; therefore the only messages available to the adversary containing the (fresh) locked by shards at this point of the protocol are pre-accept(). This is not enough to form an abort() message (which is composed of at least one pre-abort()); therefore the attacker cannot trick any shard to abort the transaction. ∎
c.2 Proof of Theorem 2
Under Honest Shard assumption, Byzcuit ignores replayed accept() and abort() messages.
Figure 6 shows that shards only act upon accept() and abort() messages if they have the pair saved in their local cache121212Contrarily to S-BAC and Atomix, all Byzcuit shards have the pair in their local cache after as they all participate to the first phase of the protocol.. Shards save a pair in their local cache upon emitting pre-accept() or pre-abort(), and delete it at the end of the protocol; therefore the only attack windows where the adversary can replay accept() and abort() messages is while the transaction (associated with ) is being processed by the second phase of Byzcuit. This forces the attacker to operates under the same conditions as Theorem 1, which is proven secure in Section C.1. ∎
Appendix D Security & Correctness of Byzcuit
We show that Byzcuit guarantees liveness, consistency, and validity similarly to S-BAC.
We rely on the liveness properties of the byzantine agreement (shards with only nodes eventually reach consensus on a sequence), and the broadcast from nodes of shards to all other nodes of shards, channelled through the Transaction Manager. Assuming has been given to an honest node, it will be sequenced withing an honest shard BFT sequence, and thus a pre-accept() or pre-abort() will be sent from the honest nodes of this shard, aggregated into accept() or abort(), and sent to the nodes of the other concerned shards. Upon receiving these messages the honest nodes from other shards will process the transaction within their shards, and the BFT will eventually sequence it. Thus the user will eventually receive a decision from at least nodes of a shard. ∎
A transaction is accepted only if some nodes receive accept(), which presupposes all shards have provided enough evidence to conclude pre-accept() for each of them. Two conflicting transaction, sharing an input, must share a shard of at least concerned nodes for the common object—with at most of them being malicious. Without loss of generality upon receiving the pre-accept() message for the first transaction, this shard will sequence it, and the honest nodes will emit messages for all—and will lock this object until the two phase protocol concludes. Any subsequent attempt to pre-accept() for a conflicting will result in a pre-abort() and cannot yield a accept, if all other shards are honest majority too. After completion of the first accept() the shard removes the object from the active set, and thus subsequent would also lead to pre-abort(). Thus there is no path in the chain of possible interleavings of the executions of two conflicting transactions that leads to them both being committed. ∎
A transaction is committed only if some nodes conclude that accept(), which presupposes all shards have provided enough evidence to conclude pre-accept() for each of them. The concerned nodes include at least one shard per input object for the transaction; for any contract logic represented in the transaction, at least one of those shards will be managing object from that contract. Each shard checks the validity rules for the objects they manage (ensuring they are active) and the contracts those objects are part of (ensuring the transaction is valid with respect to the contract logic) in order to pre-accept(). Thus if all shards say pre-accept() to conclude that accept(), all object have been checked as active, and all the contract calls within the transaction have been checked by at least one shard—whose decision is honest due to at most faulty nodes. If even a single object is inactive or locked, or a single trace for a contract fails to check, then the honest nodes in the shard will emit pre-abort(), and the final decision will be abort(). ∎