From 789a653495f2e41d449d28e3935b15ab4b21e147 Mon Sep 17 00:00:00 2001 From: Andrew Helwer Date: Thu, 1 Feb 2024 11:31:24 -0500 Subject: [PATCH] Move some specs from remote spec table into local repo (#116) Added: - Murat's PlusCal version of Lamport & Gray's analysis of Paxos vs. Two-Phase Commit - Voucher Transaction System - Atomic Commitment Protocol - Byzantine Paxos Signed-off-by: Andrew Helwer <2n8rn1w1f@mozmail.com> --- .ciignore | 5 + .github/scripts/format_markdown_table.py | 34 +- .github/scripts/generate_manifest.py | 5 +- .github/workflows/CI.yml | 2 + README.md | 230 +- manifest-schema.json | 7 +- manifest.json | 487 +++- specifications/2PCwithBTM/README.md | 10 - specifications/802.16/README.md | 9 - specifications/acp-nb-wrong/README.md | 7 - specifications/acp-nb/README.md | 7 - specifications/acp-sb/README.md | 7 - specifications/acp/ACP_NB.tla | 177 ++ specifications/acp/ACP_NB_TLC.cfg | 36 + specifications/acp/ACP_NB_TLC.tla | 18 + specifications/acp/ACP_NB_WRONG_TLC.cfg | 23 + specifications/acp/ACP_NB_WRONG_TLC.tla | 121 + specifications/acp/ACP_SB.tla | 402 ++++ specifications/acp/ACP_SB_TLC.cfg | 37 + specifications/acp/ACP_SB_TLC.tla | 18 + specifications/acp/README.md | 8 + specifications/byihive/LICENSE | 201 ++ specifications/byihive/README.md | 10 +- specifications/byihive/VoucherCancel.cfg | 10 + specifications/byihive/VoucherCancel.tla | 286 +++ specifications/byihive/VoucherIssue.cfg | 9 + specifications/byihive/VoucherIssue.tla | 287 +++ specifications/byihive/VoucherLifeCycle.cfg | 5 + specifications/byihive/VoucherLifeCycle.tla | 107 + specifications/byihive/VoucherRedeem.cfg | 10 + specifications/byihive/VoucherRedeem.tla | 284 +++ specifications/byihive/VoucherTransfer.cfg | 10 + specifications/byihive/VoucherTransfer.tla | 293 +++ specifications/byzpaxos/BPConProof.cfg | 11 + specifications/byzpaxos/BPConProof.tla | 2127 +++++++++++++++++ specifications/byzpaxos/Consensus.cfg | 6 + specifications/byzpaxos/Consensus.tla | 278 +++ specifications/byzpaxos/PConProof.cfg | 9 + specifications/byzpaxos/PConProof.tla | 613 +++++ specifications/byzpaxos/README.md | 10 +- specifications/byzpaxos/VoteProof.cfg | 10 + specifications/byzpaxos/VoteProof.tla | 1496 ++++++++++++ .../transaction_commit/2PCwithBTM.cfg | 7 + .../transaction_commit/2PCwithBTM.tla | 196 ++ 44 files changed, 7673 insertions(+), 252 deletions(-) delete mode 100644 specifications/2PCwithBTM/README.md delete mode 100644 specifications/802.16/README.md delete mode 100644 specifications/acp-nb-wrong/README.md delete mode 100644 specifications/acp-nb/README.md delete mode 100644 specifications/acp-sb/README.md create mode 100644 specifications/acp/ACP_NB.tla create mode 100644 specifications/acp/ACP_NB_TLC.cfg create mode 100644 specifications/acp/ACP_NB_TLC.tla create mode 100644 specifications/acp/ACP_NB_WRONG_TLC.cfg create mode 100644 specifications/acp/ACP_NB_WRONG_TLC.tla create mode 100644 specifications/acp/ACP_SB.tla create mode 100644 specifications/acp/ACP_SB_TLC.cfg create mode 100644 specifications/acp/ACP_SB_TLC.tla create mode 100644 specifications/acp/README.md create mode 100644 specifications/byihive/LICENSE create mode 100644 specifications/byihive/VoucherCancel.cfg create mode 100644 specifications/byihive/VoucherCancel.tla create mode 100644 specifications/byihive/VoucherIssue.cfg create mode 100644 specifications/byihive/VoucherIssue.tla create mode 100644 specifications/byihive/VoucherLifeCycle.cfg create mode 100644 specifications/byihive/VoucherLifeCycle.tla create mode 100644 specifications/byihive/VoucherRedeem.cfg create mode 100644 specifications/byihive/VoucherRedeem.tla create mode 100644 specifications/byihive/VoucherTransfer.cfg create mode 100644 specifications/byihive/VoucherTransfer.tla create mode 100644 specifications/byzpaxos/BPConProof.cfg create mode 100644 specifications/byzpaxos/BPConProof.tla create mode 100644 specifications/byzpaxos/Consensus.cfg create mode 100644 specifications/byzpaxos/Consensus.tla create mode 100644 specifications/byzpaxos/PConProof.cfg create mode 100644 specifications/byzpaxos/PConProof.tla create mode 100644 specifications/byzpaxos/VoteProof.cfg create mode 100644 specifications/byzpaxos/VoteProof.tla create mode 100644 specifications/transaction_commit/2PCwithBTM.cfg create mode 100644 specifications/transaction_commit/2PCwithBTM.tla diff --git a/.ciignore b/.ciignore index 2887d259..9f83b4a4 100644 --- a/.ciignore +++ b/.ciignore @@ -5,3 +5,8 @@ # Example: specifications/doesnotexist +# Ignore submodules to ensure deterministic local execution +specifications/BlockingQueue +specifications/CCF +specifications/azure-cosmos-tla + diff --git a/.github/scripts/format_markdown_table.py b/.github/scripts/format_markdown_table.py index c1c7ab88..361e3c22 100644 --- a/.github/scripts/format_markdown_table.py +++ b/.github/scripts/format_markdown_table.py @@ -18,42 +18,51 @@ columns = ['name', 'authors', 'beginner', 'proof', 'tlc', 'pcal', 'apalache'] -def get_column(row, index): +def get_column(row, col_name): ''' Gets the cell of the given column in the given row. ''' - return row.children[columns.index(index)].children[0] + return row.children[columns.index(col_name)].children[0] -def remove_column(table, col_index): +def remove_column(table, col_name): ''' Removes the column of the given index from the table. ''' - index = columns.index(col_index) + index = columns.index(col_name) table.header.children.pop(index) table.column_align.pop(index) for row in table.children: row.children.pop(index) -def blank_column(table, col_index): +def blank_column(table, col_name): ''' Removes all data in the given column. ''' - index = columns.index(col_index) + index = columns.index(col_name) for row in table.children: row.children[index].children = [] -def swap_columns(table, first_col_index, second_col_index): +def duplicate_column(table, col_name): + ''' + Duplicates the given column. + ''' + index = columns.index(col_name) + table.header.children.insert(index, table.header.children[index]) + table.column_align.insert(index, table.column_align[index]) + for row in table.children: + row.children.insert(index, row.children[index]) + +def swap_columns(table, first_col_name, second_col_name): ''' Swaps two columns in a table. ''' - first = columns.index(first_col_index) - second = columns.index(second_col_index) + first = columns.index(first_col_name) + second = columns.index(second_col_name) table.header.children[second], table.header.children[first] = table.header.children[first], table.header.children[second] table.column_align[second], table.column_align[first] = table.column_align[first], table.column_align[second] for row in table.children: row.children[second], row.children[first] = row.children[first], row.children[second] - def format_table(table): ''' All table transformations should go here. @@ -65,8 +74,9 @@ def format_document(document): All document transformations should go here. ''' # Gets table of local specs - table = next((child for child in document.children if isinstance(child, Table))) - format_table(table) + local_table, remote_table = [child for child in document.children if isinstance(child, Table)] + #format_table(local_table) + #format_table(remote_table) # Read, format, write # Need to both parse & render within same MarkdownRenderer context to preserve other formatting diff --git a/.github/scripts/generate_manifest.py b/.github/scripts/generate_manifest.py index b8c6fe60..b3a8463a 100644 --- a/.github/scripts/generate_manifest.py +++ b/.github/scripts/generate_manifest.py @@ -78,7 +78,7 @@ def generate_new_manifest(examples_root, ignored_dirs, parser, queries): 'path': to_posix(spec_path), 'title': spec_name, 'description': '', - 'source': '', + 'sources': [], 'authors': [], 'tags': [], 'modules': [ @@ -116,10 +116,9 @@ def find_corresponding_spec(old_spec, new_manifest): return specs[0] if any(specs) else None def integrate_spec_info(old_spec, new_spec): - fields = ['title', 'description', 'source', 'tags'] + fields = ['title', 'description', 'authors', 'sources', 'tags'] for field in fields: new_spec[field] = old_spec[field] - new_spec['authors'] = old_spec['authors'] def find_corresponding_module(old_module, new_spec): modules = [ diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index c6b491f2..e5e58a80 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -180,11 +180,13 @@ jobs: specifications/PaxosHowToWinATuringAward/Consensus.tla specifications/lamport_mutex/LamportMutex_proofs.tla specifications/ewd998/EWD998_proof.tla + specifications/byzpaxos/VoteProof.tla # Long-running; see https://github.com/tlaplus/tlapm/issues/85 specifications/LoopInvariance/Quicksort.tla specifications/LoopInvariance/SumSequence.tla specifications/bcastByz/bcastByz.tla specifications/MisraReachability/ReachabilityProofs.tla + specifications/byzpaxos/BPConProof.tla # Takes about 30 minutes ) python $SCRIPT_DIR/check_proofs.py \ --tlapm_path deps/tlapm-install \ diff --git a/README.md b/README.md index c0490e3b..653ddff1 100644 --- a/README.md +++ b/README.md @@ -16,124 +16,122 @@ All specs in this repository are subject to CI validation to ensure quality. ## Examples Included Here Here is a list of specs included in this repository, with links to the relevant directory and flags for various features: -| Name | Author(s) | Beginner | TLAPS Proof | PlusCal | TLC Model | Apalache | -| --------------------------------------------------------------------------------------------------- | ------------------------------------------ | :------: | :---------: | :-----: | :-------: | :------: | -| [Teaching Concurrency](specifications/TeachingConcurrency) | Leslie Lamport | ✔ | ✔ | ✔ | ✔ | | -| [Loop Invariance](specifications/LoopInvariance) | Leslie Lamport | ✔ | ✔ | ✔ | ✔ | | -| [Learn TLA⁺ Proofs](specifications/LearnProofs) | Andrew Helwer | ✔ | ✔ | ✔ | ✔ | | -| [Boyer-Moore Majority Vote](specifications/Majority) | Stephan Merz | ✔ | ✔ | | ✔ | | -| [Proof x+x is Even](specifications/sums_even) | Stephan Merz | ✔ | ✔ | | ✔ | | -| [The N-Queens Puzzle](specifications/N-Queens) | Stephan Merz | ✔ | | ✔ | ✔ | | -| [The Dining Philosophers Problem](specifications/DiningPhilosophers) | Jeff Hemphill | ✔ | | ✔ | ✔ | | -| [The Car Talk Puzzle](specifications/CarTalkPuzzle) | Leslie Lamport | ✔ | | | ✔ | | -| [The Die Hard Problem](specifications/DieHard) | Leslie Lamport | ✔ | | | ✔ | | -| [The Prisoners & Switches Puzzle](specifications/Prisoners) | Leslie Lamport | ✔ | | | ✔ | | -| [Specs from Specifying Systems](specifications/SpecifyingSystems) | Leslie Lamport | ✔ | | | ✔ | | -| [The Tower of Hanoi Puzzle](specifications/tower_of_hanoi) | Markus Kuppe, Alexander Niederbühl | ✔ | | | ✔ | | -| [Missionaries and Cannibals](specifications/MissionariesAndCannibals) | Leslie Lamport | ✔ | | | ✔ | | -| [The Coffee Can Bean Problem](specifications/CoffeeCan) | Andrew Helwer | ✔ | | | ✔ | | -| [Stone Scale Puzzle](specifications/Stones) | Leslie Lamport | ✔ | | | ✔ | | -| [The Boulangerie Algorithm](specifications/Bakery-Boulangerie) | Leslie Lamport, Stephan Merz | | ✔ | ✔ | ✔ | | -| [Misra Reachability Algorithm](specifications/MisraReachability) | Leslie Lamport | | ✔ | ✔ | ✔ | | -| [EWD840: Termination Detection in a Ring](specifications/ewd840) | Stephan Merz | | ✔ | | ✔ | | -| [EWD998: Termination Detection in a Ring with Asynchronous Message Delivery](specifications/ewd998) | Stephan Merz, Markus Kuppe | | ✔ | | ✔ | | -| [The Paxos Protocol](specifications/Paxos) | Leslie Lamport | | ✔ | | ✔ | | -| [Asynchronous Reliable Broadcast](specifications/bcastByz) | Thanh Hai Tran, Igor Konnov, Josef Widder | | ✔ | | ✔ | | -| [Distributed Mutual Exclusion](specifications/lamport_mutex) | Stephan Merz | | ✔ | | ✔ | | -| [Two-Phase Handshaking](specifications/TwoPhase) | Leslie Lamport, Stephan Merz | | ✔ | | ✔ | | -| [Paxos (How to Win a Turing Award)](specifications/PaxosHowToWinATuringAward) | Leslie Lamport | | ✔ | | ✔ | | -| [Dijkstra's Mutual Exclusion Algorithm](specifications/dijkstra-mutex) | Leslie Lamport | | | ✔ | ✔ | | -| [The Echo Algorithm](specifications/echo) | Stephan Merz | | | ✔ | ✔ | | -| [The TLC Safety Checking Algorithm](specifications/TLC) | Markus Kuppe | | | ✔ | ✔ | | -| [EWD687a: Detecting Termination in Distributed Computations](specifications/ewd687a) | Stephan Merz, Leslie Lamport, Markus Kuppe | | | ✔ | ✔ | | -| [The Slush Protocol](specifications/SlushProtocol) | Andrew Helwer | | | ✔ | ✔ | | -| [Minimal Circular Substring](specifications/LeastCircularSubstring) | Andrew Helwer | | | ✔ | ✔ | | -| [Snapshot Key-Value Store](specifications/KeyValueStore) | Andrew Helwer, Murat Demirbas | | | ✔ | ✔ | | -| [Chang-Roberts Algorithm for Leader Election in a Ring](specifications/chang_roberts) | Stephan Merz | | | ✔ | ✔ | | -| [Resource Allocator](specifications/allocator) | Stephan Merz | | | | ✔ | | -| [Transitive Closure](specifications/TransitiveClosure) | Stephan Merz | | | | ✔ | | -| [Sliding Block Puzzle](specifications/SlidingPuzzles) | Mariusz Ryndzionek | | | | ✔ | | -| [Single-Lane Bridge Problem](specifications/SingleLaneBridge) | Younes Akhouayri | | | | ✔ | | -| [Huang's Algorithm](specifications/Huang) | Markus Kuppe | | | | ✔ | | -| [The Knuth-Yao Method](specifications/KnuthYao) | Ron Pressler, Markus Kuppe | | | | ✔ | | -| [EWD 426: Token Stabilization](specifications/ewd426) | Murat Demirbas, Markus Kuppe | | | | ✔ | | -| [Multi-Car Elevator System](specifications/MultiCarElevator) | Andrew Helwer | | | | ✔ | | -| [Nano Blockchain Protocol](specifications/NanoBlockchain) | Andrew Helwer | | | | ✔ | | -| [Software-Defined Perimeter](specifications/SDP_Verification) | Luming Dong, Zhi Niu | | | | ✔ | | -| [Simplified Fast Paxos](specifications/SimplifiedFastPaxos) | Lim Ngian Xin Terry, Gaurav Gandhi | | | | ✔ | | -| [Checkpoint Coordination](specifications/CheckpointCoordination) | Andrew Helwer | | | | ✔ | | -| [Finitizing Monotonic Systems](specifications/FiniteMonotonic) | Andrew Helwer | | | | ✔ | | -| [The Readers-Writers Problem](specifications/ReadersWriters) | Isaac DeFrain | | | | ✔ | | -| [Einstein's Riddle](specifications/EinsteinRiddle) | Isaac DeFrain | | | | ✔ | ✔ | -| [Asynchronous Byzantine Consensus](specifications/aba-asyn-byz) | Thanh Hai Tran, Igor Konnov, Josef Widder | | | | ✔ | | -| [Folklore Reliable Broadcast](specifications/bcastFolklore) | Thanh Hai Tran, Igor Konnov, Josef Widder | | | | ✔ | | -| [The Bosco Byzantine Consensus Algorithm](specifications/bosco) | Thanh Hai Tran, Igor Konnov, Josef Widder | | | | ✔ | | -| [Consensus in One Communication Step](specifications/c1cs) | Thanh Hai Tran, Igor Konnov, Josef Widder | | | | ✔ | | -| [One-Step Consensus with Zero-Degradation](specifications/cf1s-folklore) | Thanh Hai Tran, Igor Konnov, Josef Widder | | | | ✔ | | -| [Failure Detector](specifications/detector_chan96) | Thanh Hai Tran, Igor Konnov, Josef Widder | | | | ✔ | | -| [Asynchronous Non-Blocking Atomic Commit](specifications/nbacc_ray97) | Thanh Hai Tran, Igor Konnov, Josef Widder | | | | ✔ | | -| [Asynchronous Non-Blocking Atomic Commitment with Failure Detectors](specifications/nbacg_guer01) | Thanh Hai Tran, Igor Konnov, Josef Widder | | | | ✔ | | -| [Spanning Tree Broadcast Algorithm](specifications/spanning) | Thanh Hai Tran, Igor Konnov, Josef Widder | | | | ✔ | | -| [SWMR Shared Memory Disk Paxos](specifications/diskpaxos) | Leslie Lamport, Giuliano Losa | | | | ✔ | | -| [Transaction Commit Models](specifications/transaction_commit) | Leslie Lamport, Jim Gray | | | | ✔ | | -| [Span Tree Exercise](specifications/SpanningTree) | Leslie Lamport | | | | ✔ | | -| [The Cigarette Smokers Problem](specifications/CigaretteSmokers) | Mariusz Ryndzionek | | | | ✔ | | -| [Conway's Game of Life](specifications/GameOfLife) | Mariusz Ryndzionek | | | | ✔ | | -| [Chameneos, a Concurrency Game](specifications/Chameneos) | Mariusz Ryndzionek | | | | ✔ | | -| [PCR Testing for Snippets of DNA](specifications/glowingRaccoon) | Martin Harrison | | | | ✔ | | -| [TLA⁺ Level Checking](specifications/LevelChecking) | Leslie Lamport | | | | | | -| [Condition-Based Consensus](specifications/cbc_max) | Thanh Hai Tran, Igor Konnov, Josef Widder | | | | | | +<<<<<<< HEAD +| Name | Author(s) | Beginner | TLAPS Proof | PlusCal | TLC Model | Apalache | +| --------------------------------------------------------------------------------------------------- | --------------------------------------------------- | :------: | :---------: | :-----: | :-------: | :------: | +| [Teaching Concurrency](specifications/TeachingConcurrency) | Leslie Lamport | ✔ | ✔ | ✔ | ✔ | | +| [Loop Invariance](specifications/LoopInvariance) | Leslie Lamport | ✔ | ✔ | ✔ | ✔ | | +| [Learn TLA⁺ Proofs](specifications/LearnProofs) | Andrew Helwer | ✔ | ✔ | ✔ | ✔ | | +| [Boyer-Moore Majority Vote](specifications/Majority) | Stephan Merz | ✔ | ✔ | | ✔ | | +| [Proof x+x is Even](specifications/sums_even) | Stephan Merz | ✔ | ✔ | | ✔ | | +| [The N-Queens Puzzle](specifications/N-Queens) | Stephan Merz | ✔ | | ✔ | ✔ | | +| [The Dining Philosophers Problem](specifications/DiningPhilosophers) | Jeff Hemphill | ✔ | | ✔ | ✔ | | +| [The Car Talk Puzzle](specifications/CarTalkPuzzle) | Leslie Lamport | ✔ | | | ✔ | | +| [The Die Hard Problem](specifications/DieHard) | Leslie Lamport | ✔ | | | ✔ | | +| [The Prisoners & Switches Puzzle](specifications/Prisoners) | Leslie Lamport | ✔ | | | ✔ | | +| [Specs from Specifying Systems](specifications/SpecifyingSystems) | Leslie Lamport | ✔ | | | ✔ | | +| [The Tower of Hanoi Puzzle](specifications/tower_of_hanoi) | Markus Kuppe, Alexander Niederbühl | ✔ | | | ✔ | | +| [Missionaries and Cannibals](specifications/MissionariesAndCannibals) | Leslie Lamport | ✔ | | | ✔ | | +| [Stone Scale Puzzle](specifications/Stones) | Leslie Lamport | ✔ | | | ✔ | | +| [The Coffee Can Bean Problem](specifications/CoffeeCan) | Andrew Helwer | ✔ | | | ✔ | | +| [The Boulangerie Algorithm](specifications/Bakery-Boulangerie) | Leslie Lamport, Stephan Merz | | ✔ | ✔ | ✔ | | +| [Misra Reachability Algorithm](specifications/MisraReachability) | Leslie Lamport | | ✔ | ✔ | ✔ | | +| [Byzantizing Paxos by Refinement](specifications/byzpaxos) | Leslie Lamport | | ✔ | ✔ | ✔ | | +| [EWD840: Termination Detection in a Ring](specifications/ewd840) | Stephan Merz | | ✔ | | ✔ | | +| [EWD998: Termination Detection in a Ring with Asynchronous Message Delivery](specifications/ewd998) | Stephan Merz, Markus Kuppe | | ✔ | | ✔ | | +| [The Paxos Protocol](specifications/Paxos) | Leslie Lamport | | ✔ | | ✔ | | +| [Asynchronous Reliable Broadcast](specifications/bcastByz) | Thanh Hai Tran, Igor Konnov, Josef Widder | | ✔ | | ✔ | | +| [Distributed Mutual Exclusion](specifications/lamport_mutex) | Stephan Merz | | ✔ | | ✔ | | +| [Two-Phase Handshaking](specifications/TwoPhase) | Leslie Lamport, Stephan Merz | | ✔ | | ✔ | | +| [Paxos (How to Win a Turing Award)](specifications/PaxosHowToWinATuringAward) | Leslie Lamport | | ✔ | | ✔ | | +| [Dijkstra's Mutual Exclusion Algorithm](specifications/dijkstra-mutex) | Leslie Lamport | | | ✔ | ✔ | | +| [The Echo Algorithm](specifications/echo) | Stephan Merz | | | ✔ | ✔ | | +| [The TLC Safety Checking Algorithm](specifications/TLC) | Markus Kuppe | | | ✔ | ✔ | | +| [EWD687a: Detecting Termination in Distributed Computations](specifications/ewd687a) | Stephan Merz, Leslie Lamport, Markus Kuppe | | | ✔ | ✔ | | +| [Transaction Commit Models](specifications/transaction_commit) | Leslie Lamport, Jim Gray, Murat Demirbas | | | ✔ | ✔ | | +| [The Slush Protocol](specifications/SlushProtocol) | Andrew Helwer | | | ✔ | ✔ | | +| [Minimal Circular Substring](specifications/LeastCircularSubstring) | Andrew Helwer | | | ✔ | ✔ | | +| [Snapshot Key-Value Store](specifications/KeyValueStore) | Andrew Helwer, Murat Demirbas | | | ✔ | ✔ | | +| [Chang-Roberts Algorithm for Leader Election in a Ring](specifications/chang_roberts) | Stephan Merz | | | ✔ | ✔ | | +| [Resource Allocator](specifications/allocator) | Stephan Merz | | | | ✔ | | +| [Transitive Closure](specifications/TransitiveClosure) | Stephan Merz | | | | ✔ | | +| [Atomic Commitment Protocol](specifications/acp) | Stephan Merz | | | | ✔ | | +| [SWMR Shared Memory Disk Paxos](specifications/diskpaxos) | Leslie Lamport, Giuliano Losa | | | | ✔ | | +| [Span Tree Exercise](specifications/SpanningTree) | Leslie Lamport | | | | ✔ | | +| [The Knuth-Yao Method](specifications/KnuthYao) | Ron Pressler, Markus Kuppe | | | | ✔ | | +| [Huang's Algorithm](specifications/Huang) | Markus Kuppe | | | | ✔ | | +| [EWD 426: Token Stabilization](specifications/ewd426) | Murat Demirbas, Markus Kuppe | | | | ✔ | | +| [Sliding Block Puzzle](specifications/SlidingPuzzles) | Mariusz Ryndzionek | | | | ✔ | | +| [Single-Lane Bridge Problem](specifications/SingleLaneBridge) | Younes Akhouayri | | | | ✔ | | +| [Software-Defined Perimeter](specifications/SDP_Verification) | Luming Dong, Zhi Niu | | | | ✔ | | +| [Simplified Fast Paxos](specifications/SimplifiedFastPaxos) | Lim Ngian Xin Terry, Gaurav Gandhi | | | | ✔ | | +| [Checkpoint Coordination](specifications/CheckpointCoordination) | Andrew Helwer | | | | ✔ | | +| [Finitizing Monotonic Systems](specifications/FiniteMonotonic) | Andrew Helwer | | | | ✔ | | +| [Multi-Car Elevator System](specifications/MultiCarElevator) | Andrew Helwer | | | | ✔ | | +| [Nano Blockchain Protocol](specifications/NanoBlockchain) | Andrew Helwer | | | | ✔ | | +| [The Readers-Writers Problem](specifications/ReadersWriters) | Isaac DeFrain | | | | ✔ | | +| [Einstein's Riddle](specifications/EinsteinRiddle) | Isaac DeFrain, Giuliano Losa | | | | ✔ | ✔ | +| [Asynchronous Byzantine Consensus](specifications/aba-asyn-byz) | Thanh Hai Tran, Igor Konnov, Josef Widder | | | | ✔ | | +| [Folklore Reliable Broadcast](specifications/bcastFolklore) | Thanh Hai Tran, Igor Konnov, Josef Widder | | | | ✔ | | +| [The Bosco Byzantine Consensus Algorithm](specifications/bosco) | Thanh Hai Tran, Igor Konnov, Josef Widder | | | | ✔ | | +| [Consensus in One Communication Step](specifications/c1cs) | Thanh Hai Tran, Igor Konnov, Josef Widder | | | | ✔ | | +| [One-Step Consensus with Zero-Degradation](specifications/cf1s-folklore) | Thanh Hai Tran, Igor Konnov, Josef Widder | | | | ✔ | | +| [Failure Detector](specifications/detector_chan96) | Thanh Hai Tran, Igor Konnov, Josef Widder | | | | ✔ | | +| [Asynchronous Non-Blocking Atomic Commit](specifications/nbacc_ray97) | Thanh Hai Tran, Igor Konnov, Josef Widder | | | | ✔ | | +| [Asynchronous Non-Blocking Atomic Commitment with Failure Detectors](specifications/nbacg_guer01) | Thanh Hai Tran, Igor Konnov, Josef Widder | | | | ✔ | | +| [Spanning Tree Broadcast Algorithm](specifications/spanning) | Thanh Hai Tran, Igor Konnov, Josef Widder | | | | ✔ | | +| [The Cigarette Smokers Problem](specifications/CigaretteSmokers) | Mariusz Ryndzionek | | | | ✔ | | +| [Conway's Game of Life](specifications/GameOfLife) | Mariusz Ryndzionek | | | | ✔ | | +| [Chameneos, a Concurrency Game](specifications/Chameneos) | Mariusz Ryndzionek | | | | ✔ | | +| [PCR Testing for Snippets of DNA](specifications/glowingRaccoon) | Martin Harrison | | | | ✔ | | +| [RFC 3506: Voucher Transaction System](specifications/byihive) | Santhosh Raju, Cherry G. Mathew, Fransisca Andriani | | | | ✔ | | +| [TLA⁺ Level Checking](specifications/LevelChecking) | Leslie Lamport | | | | | | +| [Condition-Based Consensus](specifications/cbc_max) | Thanh Hai Tran, Igor Konnov, Josef Widder | | | | | | ## Examples Elsewhere Here is a list of specs stored in locations outside this repository, including submodules. They are not covered by CI testing so it is possible they contain errors, the reported details are incorrect, or they are no longer available. Ideally these will be moved into this repo over time. -| Name | Description | Author(s) | TLAPS Proof? | TLC Model? | PlusCal? | Apalache? | -| --------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ | :----------: | :--------: | :------: | :-------: | -| [2PCwithBTM](specifications/2PCwithBTM) | A modified version of P2TCommit (Gray & Lamport, 2006) | Murat Demirbas | | ✔ | ✔ | | -| [802.16](specifications/802.16) | IEEE 802.16 WiMAX Protocols | Prasad Narayana, Ruiming Chen, Yao Zhao, Yan Chen, Zhi (Judy) Fu, and Hai Zhou | | ✔ | | | -| [acp-nb](specifications/acp-nb) | Non-blocking atomic commitment with a reliable broadcast (Babaoğlu & Toueg, 1993) | Stephan Merz | | ✔ | | | -| [acp-nb-wrong](specifications/acp-nb-wrong) | Wrong version of the non-blocking atomic commitment with a reliable broadcast (Babaoğlu & Toueg, 1993) | Stephan Merz | | ✔ | | | -| [acp-sb](specifications/acp-sb) | Non-blocking atomic commitment with a simple broadcast (Babaoğlu & Toueg, 1993) | Stephan Merz | | ✔ | | | -| [async-comm](specifications/async-comm) | The diversity of asynchronous communication (Chevrou et al., 2016) | Florent Chevrou, Aurélie Hurault, Philippe Quéinnec | | ✔ | | | -| [byihive](specifications/byihive) | Based on RFC3506 - Requirements and Design for Voucher Trading System (Fujimura & Eastlake) | Santhosh Raju | | ✔ | | | -| [byzpaxos](specifications/byzpaxos) | Byzantizing Paxos by Refinement (Lamport, 2011) | Leslie Lamport | | ✔ | | | -| [Caesar](specifications/Caesar) | Multi-leader generalized consensus protocol (Arun et al., 2017) | Giuliano Losa | | ✔ | ✔ | | -| [CASPaxos](specifications/CASPaxos) | An extension of the single-decree Paxos algorithm to a compare-and-swap type register (Rystsov) | Tobias Schottdorf | | ✔ | | | -| [DataPort](specifications/DataPort) | Dataport protocal 505.89PT, only PDF files (Biggs & Noriaki, 2016) | Geoffrey Biggs, Noriaki Ando | | | | | -| [egalitarian-paxos](specifications/egalitarian-paxos) | Leaderless replication protocol based on Paxos (Moraru et al., 2013) | Iulian Moraru | | ✔ | | | -| [fastpaxos](specifications/fastpaxos) | An extension of the classic Paxos algorithm, only PDF files (Lamport, 2006) | Leslie Lamport | | | | | -| [fpaxos](specifications/fpaxos) | A variant of Paxos with flexible quorums (Howard et al., 2017) | Heidi Howard | | ✔ | | | -| [HLC](specifications/HLC) | Hybrid logical clocks and hybrid vector clocks (Demirbas et al., 2014) | Murat Demirbas | | ✔ | ✔ | | -| [L1](specifications/L1) | Data center network L1 switch protocol, only PDF files (Thacker) | Tom Rodeheffer | | | | | -| [leaderless](specifications/leaderless) | Leaderless generalized-consensus algorithms (Losa, 2016) | Giuliano Losa | | ✔ | ✔ | | -| [losa_ap](specifications/losa_ap) | The assignment problem, a variant of the allocation problem (Delporte-Gallet, 2018) | Giuliano Losa | | ✔ | ✔ | | -| [losa_rda](specifications/losa_rda) | Applying peculative linearizability to fault-tolerant message-passing algorithms and shared-memory consensus, only PDF files (Losa, 2014) | Giuliano Losa | | | | | -| [m2paxos](specifications/m2paxos) | Multi-leader consensus protocols (Peluso et al., 2016) | Giuliano Losa | | ✔ | | | -| [mongo-repl-tla](specifications/mongo-repl-tla) | A simplified part of Raft in MongoDB (Ongaro, 2014) | Siyuan Zhou | | ✔ | | | -| [MultiPaxos](specifications/MultiPaxos) | The abstract specification of Generalized Paxos (Lamport, 2004) | Giuliano Losa | | ✔ | | | -| [naiad](specifications/naiad) | Naiad clock protocol, only PDF files (Murray et al., 2013) | Tom Rodeheffer | | ✔ | | | -| [nfc04](specifications/nfc04) | Non-functional properties of component-based software systems (Zschaler, 2010) | Steffen Zschaler | | ✔ | | | -| [raft](specifications/raft) | Raft consensus algorithm (Ongaro, 2014) | Diego Ongaro | | ✔ | | | -| [SnapshotIsolation](specifications/SnapshotIsolation) | Serializable snapshot isolation (Cahill et al., 2010) | Michael J. Cahill, Uwe Röhm, Alan D. Fekete | | ✔ | | | -| [SyncConsensus](specifications/SyncConsensus) | Synchronized round consensus algorithm (Demirbas) | Murat Demirbas | | ✔ | ✔ | | -| [Termination](specifications/Termination) | Channel-counting algorithm (Kumar, 1985) | Giuliano Losa | ✔ | ✔ | ✔ | ✔ | -| [Tla-tortoise-hare](specifications/Tla-tortoise-hare) | Robert Floyd's cycle detection algorithm | Lorin Hochstein | | ✔ | ✔ | | -| [VoldemortKV](specifications/VoldemortKV) | Voldemort distributed key value store | Murat Demirbas | | ✔ | ✔ | | -| [Tencent-Paxos](specifications/TencentPaxos) | PaxosStore: high-availability storage made practical in WeChat. Proceedings of the VLDB Endowment(Zheng et al., 2017) | Xingchen Yi, Hengfeng Wei | ✔ | ✔ | | | -| [Paxos](https://github.com/neoschizomer/Paxos) | Paxos | | | ✔ | | | -| [Lock-Free Set](https://github.com/tlaplus/tlaplus/blob/master/tlatools/org.lamport.tlatools/src/tlc2/tool/fp/OpenAddressing.tla) | PlusCal spec of a lock-Free set used by TLC | Markus Kuppe | | ✔ | ✔ | | -| [ParallelRaft](specifications/ParalleRaft) | A variant of Raft | Xiaosong Gu, Hengfeng Wei, Yu Huang | | ✔ | | | -| [CRDT-Bug](https://github.com/Alexander-N/tla-specs/tree/main/crdt-bug) | CRDT algorithm with defect and fixed version | Alexander Niederbühl | | ✔ | | | -| [asyncio-lock](https://github.com/Alexander-N/tla-specs/tree/main/asyncio-lock) | Bugs from old versions of Python's asyncio lock | Alexander Niederbühl | | ✔ | | | -| [Raft (with cluster changes)](https://github.com/dranov/raft-tla) | Raft with cluster changes, and a version with Apalache type annotations but no cluster changes | George Pîrlea, Darius Foom, Brandon Amos, Huanchen Zhang, Daniel Ricketts | | ✔ | | ✔ | -| [MET for CRDT-Redis](https://github.com/elem-azar-unis/CRDT-Redis/tree/master/MET/TLA) | Model-check the CRDT designs, then generate test cases to test CRDT implementations | Yuqi Zhang | | ✔ | ✔ | | -| [Parallel increment](https://github.com/Cjen1/tla_increment) | Parallel threads incrementing a shared variable. Demonstrates invariants, liveness, fairness and symmetry | Chris Jensen | | ✔ | | | -| [The Streamlet consensus algorithm](https://github.com/nano-o/streamlet) | Specification and model-checking of both safety and liveness properties of Streamlet with TLC | Giuliano Losa | | ✔ | ✔ | | -| [Petri Nets](https://github.com/elh/petri-tlaplus) | Instantiable Petri Nets with liveness properties | Eugene Huang | | ✔ | | | -| [CRDT](https://github.com/JYwellin/CRDT-TLA) | Specifying and Verifying CRDT Protocols | Ye Ji, Hengfeng Wei | | ✔ | | | -| [Azure Cosmos DB](https://github.com/tlaplus/azure-cosmos-tla) | Consistency models provided by Azure Cosmos DB | Dharma Shukla, Ailidani Ailijiang, Murat Demirbas, Markus Kuppe | | ✔ | ✔ | | -| [Blocking Queue](https://github.com/lemmy/BlockingQueue) | BlockingQueue | Markus Kuppe | ✔ | ✔ | | | +| Spec | Details | Author(s) | Beginner | TLAPS Proof | TLC Model | PlusCal | Apalache | +| --------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- | :------: | :---------: | :-------: | :-----: | :------: | +| [Blocking Queue](https://github.com/lemmy/BlockingQueue) | BlockingQueue | Markus Kuppe | ✔ | ✔ | ✔ | | | +| IEEE 802.16 WiMAX Protocols | 2006, [paper](https://users.cs.northwestern.edu/~ychen/Papers/npsec06.pdf), [specs](http://list.cs.northwestern.edu/802.16/) | Prasad Narayana, Ruiming Chen, Yao Zhao, Yan Chen, Zhi (Judy) Fu, Hai Zhou | | | | | | +| On the diversity of asynchronous communication | 2016, [paper](https://dl.acm.org/doi/10.1007/s00165-016-0379-x), [specs](http://hurault.perso.enseeiht.fr/asynchronousCommunication/) | Florent Chevrou, Aurélie Hurault, Philippe Quéinnec | | | | | | +| [Caesar](specifications/Caesar) | Multi-leader generalized consensus protocol (Arun et al., 2017) | Giuliano Losa | | | ✔ | ✔ | | +| [CASPaxos](specifications/CASPaxos) | An extension of the single-decree Paxos algorithm to a compare-and-swap type register (Rystsov) | Tobias Schottdorf | | | ✔ | | | +| [DataPort](specifications/DataPort) | Dataport protocal 505.89PT, only PDF files (Biggs & Noriaki, 2016) | Geoffrey Biggs, Noriaki Ando | | | | | | +| [egalitarian-paxos](specifications/egalitarian-paxos) | Leaderless replication protocol based on Paxos (Moraru et al., 2013) | Iulian Moraru | | | ✔ | | | +| [fastpaxos](specifications/fastpaxos) | An extension of the classic Paxos algorithm, only PDF files (Lamport, 2006) | Leslie Lamport | | | | | | +| [fpaxos](specifications/fpaxos) | A variant of Paxos with flexible quorums (Howard et al., 2017) | Heidi Howard | | | ✔ | | | +| [HLC](specifications/HLC) | Hybrid logical clocks and hybrid vector clocks (Demirbas et al., 2014) | Murat Demirbas | | | ✔ | ✔ | | +| [L1](specifications/L1) | Data center network L1 switch protocol, only PDF files (Thacker) | Tom Rodeheffer | | | | | | +| [leaderless](specifications/leaderless) | Leaderless generalized-consensus algorithms (Losa, 2016) | Giuliano Losa | | | ✔ | ✔ | | +| [losa_ap](specifications/losa_ap) | The assignment problem, a variant of the allocation problem (Delporte-Gallet, 2018) | Giuliano Losa | | | ✔ | ✔ | | +| [losa_rda](specifications/losa_rda) | Applying peculative linearizability to fault-tolerant message-passing algorithms and shared-memory consensus, only PDF files (Losa, 2014) | Giuliano Losa | | | | | | +| [m2paxos](specifications/m2paxos) | Multi-leader consensus protocols (Peluso et al., 2016) | Giuliano Losa | | | ✔ | | | +| [mongo-repl-tla](specifications/mongo-repl-tla) | A simplified part of Raft in MongoDB (Ongaro, 2014) | Siyuan Zhou | | | ✔ | | | +| [MultiPaxos](specifications/MultiPaxos) | The abstract specification of Generalized Paxos (Lamport, 2004) | Giuliano Losa | | | ✔ | | | +| [naiad](specifications/naiad) | Naiad clock protocol, only PDF files (Murray et al., 2013) | Tom Rodeheffer | | | ✔ | | | +| [nfc04](specifications/nfc04) | Non-functional properties of component-based software systems (Zschaler, 2010) | Steffen Zschaler | | | ✔ | | | +| [raft](specifications/raft) | Raft consensus algorithm (Ongaro, 2014) | Diego Ongaro | | | ✔ | | | +| [SnapshotIsolation](specifications/SnapshotIsolation) | Serializable snapshot isolation (Cahill et al., 2010) | Michael J. Cahill, Uwe Röhm, Alan D. Fekete | | | ✔ | | | +| [SyncConsensus](specifications/SyncConsensus) | Synchronized round consensus algorithm (Demirbas) | Murat Demirbas | | | ✔ | ✔ | | +| [Termination](specifications/Termination) | Channel-counting algorithm (Kumar, 1985) | Giuliano Losa | | ✔ | ✔ | ✔ | ✔ | +| [Tla-tortoise-hare](specifications/Tla-tortoise-hare) | Robert Floyd's cycle detection algorithm | Lorin Hochstein | | | ✔ | ✔ | | +| [VoldemortKV](specifications/VoldemortKV) | Voldemort distributed key value store | Murat Demirbas | | | ✔ | ✔ | | +| [Tencent-Paxos](specifications/TencentPaxos) | PaxosStore: high-availability storage made practical in WeChat. Proceedings of the VLDB Endowment(Zheng et al., 2017) | Xingchen Yi, Hengfeng Wei | | ✔ | ✔ | | | +| [Paxos](https://github.com/neoschizomer/Paxos) | Paxos | | | | ✔ | | | +| [Lock-Free Set](https://github.com/tlaplus/tlaplus/blob/master/tlatools/org.lamport.tlatools/src/tlc2/tool/fp/OpenAddressing.tla) | PlusCal spec of a lock-Free set used by TLC | Markus Kuppe | | | ✔ | ✔ | | +| [ParallelRaft](specifications/ParalleRaft) | A variant of Raft | Xiaosong Gu, Hengfeng Wei, Yu Huang | | | ✔ | | | +| [CRDT-Bug](https://github.com/Alexander-N/tla-specs/tree/main/crdt-bug) | CRDT algorithm with defect and fixed version | Alexander Niederbühl | | | ✔ | | | +| [asyncio-lock](https://github.com/Alexander-N/tla-specs/tree/main/asyncio-lock) | Bugs from old versions of Python's asyncio lock | Alexander Niederbühl | | | ✔ | | | +| [Raft (with cluster changes)](https://github.com/dranov/raft-tla) | Raft with cluster changes, and a version with Apalache type annotations but no cluster changes | George Pîrlea, Darius Foom, Brandon Amos, Huanchen Zhang, Daniel Ricketts | | | ✔ | | ✔ | +| [MET for CRDT-Redis](https://github.com/elem-azar-unis/CRDT-Redis/tree/master/MET/TLA) | Model-check the CRDT designs, then generate test cases to test CRDT implementations | Yuqi Zhang | | | ✔ | ✔ | | +| [Parallel increment](https://github.com/Cjen1/tla_increment) | Parallel threads incrementing a shared variable. Demonstrates invariants, liveness, fairness and symmetry | Chris Jensen | | | ✔ | | | +| [The Streamlet consensus algorithm](https://github.com/nano-o/streamlet) | Specification and model-checking of both safety and liveness properties of Streamlet with TLC | Giuliano Losa | | | ✔ | ✔ | | +| [Petri Nets](https://github.com/elh/petri-tlaplus) | Instantiable Petri Nets with liveness properties | Eugene Huang | | | ✔ | | | +| [CRDT](https://github.com/JYwellin/CRDT-TLA) | Specifying and Verifying CRDT Protocols | Ye Ji, Hengfeng Wei | | | ✔ | | | +| [Azure Cosmos DB](https://github.com/tlaplus/azure-cosmos-tla) | Consistency models provided by Azure Cosmos DB | Dharma Shukla, Ailidani Ailijiang, Murat Demirbas, Markus Kuppe | | | ✔ | ✔ | | ## License The repository is under the MIT license and you are encouraged to publish your spec under a similarly-permissive license. @@ -147,7 +145,8 @@ Please open an issue or send an email to the [TLA+ group](https://groups.google. Do you have your own case study or TLA+ specification you'd like to share with the community? Follow these instructions: 1. Fork this repository and create a new directory under `specifications` with the name of your spec -1. Place all TLA+ files in the directory, along with their `.cfg` model files; you are encouraged to include at least one model that completes execution in less than 10 seconds +1. Place all TLA+ files in the directory, along with their `.cfg` model files +1. You are encouraged to include at least one model that completes execution in less than 10 seconds, and (if possible) a model that fails in an interesting way - for example illustrating a system design you previously attempted that was found unsound by TLC 1. Ensure name of each `.cfg` file matches the `.tla` file it models, or has its name as a prefix; for example `SpecName.tla` can have the models `SpecName.cfg` and `SpecNameLiveness.cfg`, etc. 1. Consider including a `README.md` in the spec directory explaining the significance of the spec with links to any relevant websites or publications, or integrate this info as comments in the spec itself 1. Add an entry to the table of specs included in this repo, summarizing your spec and its attributes @@ -167,7 +166,7 @@ Otherwise, follow these directions: 1. Locate your spec's entry in the [`manifest.json`](manifest.json) file and ensure the following fields are filled out: - Spec title: an appropriate title for your specification - Spec description: a short description of your specification - - Spec source: if this spec was first published elsewhere, provide a link to this location + - Spec sources: links relevant to the source of the spec (papers, blog posts, repositories) - Spec authors: a list of people who authored the spec - Spec tags: - `"beginner"` if your spec is appropriate for TLA+ newcomers @@ -182,6 +181,7 @@ Otherwise, follow these directions: - `"generate"` for model trace generation - Model result: - `"success"` if the model completes successfully + - `"assumption failure"` if the model violates an assumption - `"safety failure"` if the model violates an invariant - `"liveness failure"` if the model fails to satisfy a liveness property - `"deadlock failure"` if the model encounters deadlock diff --git a/manifest-schema.json b/manifest-schema.json index 611f72d3..cdb72739 100644 --- a/manifest-schema.json +++ b/manifest-schema.json @@ -7,13 +7,16 @@ "type": "array", "items": { "type": "object", - "required": ["title", "description", "source", "authors", "path", "tags", "modules"], + "required": ["title", "description", "sources", "authors", "path", "tags", "modules"], "additionalProperties": false, "properties": { "path": {"type": "string"}, "title": {"type": "string"}, "description": {"type": "string"}, - "source": {"type": "string"}, + "sources": { + "type": "array", + "items": {"type": "string"} + }, "authors": { "type": "array", "items": {"type": "string"} diff --git a/manifest.json b/manifest.json index 26d75f6c..e953ce0d 100644 --- a/manifest.json +++ b/manifest.json @@ -4,7 +4,7 @@ "path": "specifications/Bakery-Boulangerie", "title": "The Boulangerie Algorithm", "description": "Spec of a variant of the Bakery Algorithm", - "source": "", + "sources": [], "authors": [ "Leslie Lamport", "Stephan Merz" @@ -73,7 +73,7 @@ "path": "specifications/CarTalkPuzzle", "title": "The Car Talk Puzzle", "description": "Math puzzle involving a farmer, a stone, and a balance scale", - "source": "", + "sources": [], "authors": [ "Leslie Lamport" ], @@ -163,7 +163,9 @@ "path": "specifications/Chameneos", "title": "Chameneos, a Concurrency Game", "description": "A concurrency game requiring concurrent & symmetric cooperation", - "source": "https://github.com/mryndzionek/tlaplus_specs#chameneostla", + "sources": [ + "https://github.com/mryndzionek/tlaplus_specs#chameneostla" + ], "authors": [ "Mariusz Ryndzionek" ], @@ -193,7 +195,10 @@ "path": "specifications/CheckpointCoordination", "title": "Checkpoint Coordination", "description": "An algorithm for controlling checkpoint leases in a Paxos ring", - "source": "https://github.com/Azure/RingMaster", + "sources": [ + "https://github.com/Azure/RingMaster", + "https://ahelwer.ca/post/2023-04-05-checkpoint-coordination/" + ], "authors": [ "Andrew Helwer" ], @@ -242,7 +247,9 @@ "path": "specifications/CigaretteSmokers", "title": "The Cigarette Smokers Problem", "description": "A concurrency problem described in 1971 by Suhas Patil", - "source": "https://github.com/mryndzionek/tlaplus_specs#cigarettesmokerstla", + "sources": [ + "https://github.com/mryndzionek/tlaplus_specs#cigarettesmokerstla" + ], "authors": [ "Mariusz Ryndzionek" ], @@ -270,7 +277,9 @@ "path": "specifications/CoffeeCan", "title": "The Coffee Can Bean Problem", "description": "Math problem published by Dijkstra attributed to Carel Scholten", - "source": "https://stackoverflow.com/a/66304920/2852699", + "sources": [ + "https://stackoverflow.com/a/66304920/2852699" + ], "authors": [ "Andrew Helwer" ], @@ -322,7 +331,7 @@ "path": "specifications/DieHard", "title": "The Die Hard Problem", "description": "Obtain 4 gallons of water using only 3 and 5 gallon jugs", - "source": "", + "sources": [], "authors": [ "Leslie Lamport" ], @@ -375,7 +384,7 @@ "path": "specifications/DiningPhilosophers", "title": "The Dining Philosophers Problem", "description": "The Chandy-Misra solution to the Dining Philosophers concurrency problem", - "source": "", + "sources": [], "authors": [ "Jeff Hemphill" ], @@ -409,9 +418,10 @@ "path": "specifications/EinsteinRiddle", "title": "Einstein's Riddle", "description": "Five houses, five people, various facts, who lives in what house?", - "source": "", + "sources": [], "authors": [ - "Isaac DeFrain" + "Isaac DeFrain", + "Giuliano Losa" ], "tags": [], "modules": [ @@ -437,7 +447,9 @@ "path": "specifications/FiniteMonotonic", "title": "Finitizing Monotonic Systems", "description": "Illustration of technique for making ordinarily-infinite monotonic systems finite for the purposes of model-checking.", - "source": "https://ahelwer.ca/post/2023-11-01-tla-finite-monotonic/", + "sources": [ + "https://ahelwer.ca/post/2023-11-01-tla-finite-monotonic/" + ], "authors": [ "Andrew Helwer" ], @@ -520,7 +532,9 @@ "path": "specifications/GameOfLife", "title": "Conway's Game of Life", "description": "The famous cellular automata simulation", - "source": "https://github.com/mryndzionek/tlaplus_specs#gameoflifetla", + "sources": [ + "https://github.com/mryndzionek/tlaplus_specs#gameoflifetla" + ], "authors": [ "Mariusz Ryndzionek" ], @@ -548,7 +562,7 @@ "path": "specifications/Huang", "title": "Huang's Algorithm", "description": "An algorithm for detecting termination in a distributed system", - "source": "", + "sources": [], "authors": [ "Markus Kuppe" ], @@ -585,7 +599,7 @@ "path": "specifications/KeyValueStore", "title": "Snapshot Key-Value Store", "description": "A simple KVS implementing snapshot isolation", - "source": "", + "sources": [], "authors": [ "Andrew Helwer", "Murat Demirbas" @@ -685,7 +699,9 @@ "path": "specifications/KnuthYao", "title": "The Knuth-Yao Method", "description": "A method for simulating a fair 6-sided die using only coin flips", - "source": "https://old.reddit.com/r/tlaplus/comments/j06ohw/how_do_you_reason_about_a_probabilistic/g6owlxy/", + "sources": [ + "https://old.reddit.com/r/tlaplus/comments/j06ohw/how_do_you_reason_about_a_probabilistic/g6owlxy/" + ], "authors": [ "Markus Kuppe", "Ron Pressler" @@ -738,7 +754,7 @@ "path": "specifications/LearnProofs", "title": "Learn TLA⁺ Proofs", "description": "Some specs of very simple algorithms & formal proofs of their correctness", - "source": "", + "sources": [], "authors": [ "Andrew Helwer" ], @@ -790,7 +806,9 @@ "path": "specifications/LeastCircularSubstring", "title": "Minimal Circular Substring", "description": "Booth's 1980 algorithm to find the lexicographically-least circular substring", - "source": "", + "sources": [ + "https://ahelwer.ca/post/2023-03-30-pseudocode/" + ], "authors": [ "Andrew Helwer" ], @@ -842,7 +860,9 @@ "path": "specifications/LevelChecking", "title": "TLA⁺ Level Checking", "description": "Level-checking of TLA⁺ formulas as described in Specifying Systems", - "source": "https://github.com/tlaplus/tlaplus/blob/6bc82f7746ccdfbdf49cdef24448666d11e5e218/tlatools/org.lamport.tlatools/src/tla2sany/semantic/LevelNode.java#L354-L2778", + "sources": [ + "https://github.com/tlaplus/tlaplus/blob/6bc82f7746ccdfbdf49cdef24448666d11e5e218/tlatools/org.lamport.tlatools/src/tla2sany/semantic/LevelNode.java#L354-L2778" + ], "authors": [ "Leslie Lamport" ], @@ -861,7 +881,9 @@ "path": "specifications/LoopInvariance", "title": "Loop Invariance", "description": "Examples of finding and proving loop invariants in PlusCal", - "source": "http://lamport.azurewebsites.net/tla/proving-safety.pdf", + "sources": [ + "http://lamport.azurewebsites.net/tla/proving-safety.pdf" + ], "authors": [ "Leslie Lamport" ], @@ -941,7 +963,7 @@ "path": "specifications/Majority", "title": "Boyer-Moore Majority Vote", "description": "An efficient algorithm for detecting a majority value in a sequence", - "source": "", + "sources": [], "authors": [ "Stephan Merz" ], @@ -989,7 +1011,7 @@ "path": "specifications/MisraReachability", "title": "Misra Reachability Algorithm", "description": "A sequential algorithm for computing the set of reachable nodes in a directed graph", - "source": "", + "sources": [], "authors": [ "Leslie Lamport" ], @@ -1120,7 +1142,7 @@ "path": "specifications/MissionariesAndCannibals", "title": "Missionaries and Cannibals", "description": "Spec of a river-crossing puzzle", - "source": "", + "sources": [], "authors": [ "Leslie Lamport" ], @@ -1150,7 +1172,9 @@ "path": "specifications/MultiCarElevator", "title": "Multi-Car Elevator System", "description": "A simple multi-car elevator system servicing a multi-floor building", - "source": "https://groups.google.com/g/tlaplus/c/5Xd8kv288jE/m/IrliJIatBwAJ", + "sources": [ + "https://groups.google.com/g/tlaplus/c/5Xd8kv288jE/m/IrliJIatBwAJ" + ], "authors": [ "Andrew Helwer" ], @@ -1214,7 +1238,7 @@ "path": "specifications/N-Queens", "title": "The N-Queens Puzzle", "description": "Place N queens on an NxN chess board such that no two queens threaten each other", - "source": "", + "sources": [], "authors": [ "Stephan Merz" ], @@ -1296,7 +1320,7 @@ "path": "specifications/NanoBlockchain", "title": "Nano Blockchain Protocol", "description": "A specification of the protocol originally used by the Nano blockchain", - "source": "", + "sources": [], "authors": [ "Andrew Helwer" ], @@ -1353,7 +1377,7 @@ "path": "specifications/Paxos", "title": "The Paxos Protocol", "description": "A protocol for error-tolerant consensus", - "source": "", + "sources": [], "authors": [ "Leslie Lamport" ], @@ -1447,7 +1471,9 @@ "path": "specifications/PaxosHowToWinATuringAward", "title": "Paxos (How to Win a Turing Award)", "description": "Exercises from the lecture The Paxos Algorithm - or How to Win a Turing Award", - "source": "https://www.youtube.com/watch?v=tw3gsBms-f8", + "sources": [ + "https://www.youtube.com/watch?v=tw3gsBms-f8" + ], "authors": [ "Leslie Lamport" ], @@ -1549,7 +1575,7 @@ "path": "specifications/Prisoners", "title": "The Prisoners & Switches Puzzle", "description": "Given a room containing two switches prisoners enter one by one, they must develop a strategy to free themselves", - "source": "", + "sources": [], "authors": [ "Leslie Lamport" ], @@ -1588,7 +1614,7 @@ "path": "specifications/ReadersWriters", "title": "The Readers-Writers Problem", "description": "Controlling concurrent access to a resource", - "source": "", + "sources": [], "authors": [ "Isaac DeFrain" ], @@ -1625,7 +1651,9 @@ "path": "specifications/SDP_Verification", "title": "Software-Defined Perimeter", "description": "Specifying and verifying SDP-protocol-based zero trust architecture", - "source": "https://github.com/10227694/SDP_Verification", + "sources": [ + "https://github.com/10227694/SDP_Verification" + ], "authors": [ "Luming Dong", "Zhi Niu" @@ -1696,7 +1724,7 @@ "path": "specifications/SimplifiedFastPaxos", "title": "Simplified Fast Paxos", "description": "Spec of simplified Fast Paxos from Lamport's 2006 paper Fast Paxos", - "source": "", + "sources": [], "authors": [ "Lim Ngian Xin Terry", "Gaurav Gandhi" @@ -1747,7 +1775,7 @@ "path": "specifications/SingleLaneBridge", "title": "Single-Lane Bridge Problem", "description": "Controlling cars moving over a single-lange bridge in opposite directions", - "source": "", + "sources": [], "authors": [ "Younes Akhouayri" ], @@ -1784,7 +1812,7 @@ "path": "specifications/SlidingPuzzles", "title": "Sliding Block Puzzle", "description": "A spec of the Klotski sliding block puzzle", - "source": "", + "sources": [], "authors": [ "Mariusz Ryndzionek" ], @@ -1812,7 +1840,10 @@ "path": "specifications/SlushProtocol", "title": "The Slush Protocol", "description": "An attempt to use TLA⁺ to analyze a probabilistic protocol in the Avalanche family", - "source": "https://github.com/ahelwer/avalanche-analysis", + "sources": [ + "https://github.com/ahelwer/avalanche-analysis", + "https://ahelwer.ca/post/2020-09-11-probabilistic-distsys/" + ], "authors": [ "Andrew Helwer" ], @@ -1858,7 +1889,7 @@ "path": "specifications/SpanningTree", "title": "Span Tree Exercise", "description": "Simplified algorithm from the Lamport paper *An Assertional Correctness Proof of a Distributed Program*", - "source": "", + "sources": [], "authors": [ "Leslie Lamport" ], @@ -1937,7 +1968,9 @@ "path": "specifications/SpecifyingSystems", "title": "Specs from Specifying Systems", "description": "All specs seen in the textbook Specifying Systems by Leslie Lamport", - "source": "http://lamport.azurewebsites.net/tla/book.html", + "sources": [ + "http://lamport.azurewebsites.net/tla/book.html" + ], "authors": [ "Leslie Lamport" ], @@ -2631,7 +2664,7 @@ "path": "specifications/Stones", "title": "Stone Scale Puzzle", "description": "Alternative implementation of the Car Talk Puzzle", - "source": "", + "sources": [], "authors": [ "Leslie Lamport" ], @@ -2661,7 +2694,7 @@ "path": "specifications/TLC", "title": "The TLC Safety Checking Algorithm", "description": "Spec of the safety checking algorithm used by the TLC model-checker", - "source": "", + "sources": [], "authors": [ "Markus Kuppe" ], @@ -2700,7 +2733,9 @@ "path": "specifications/TeachingConcurrency", "title": "Teaching Concurrency", "description": "Simple problem useful for teaching and learning about concurrency", - "source": "http://lamport.azurewebsites.net/pubs/teaching-concurrency.pdf", + "sources": [ + "http://lamport.azurewebsites.net/pubs/teaching-concurrency.pdf" + ], "authors": [ "Leslie Lamport" ], @@ -2752,7 +2787,7 @@ "path": "specifications/TransitiveClosure", "title": "Transitive Closure", "description": "Spec defining the transitive closure of a relation", - "source": "", + "sources": [], "authors": [ "Stephan Merz" ], @@ -2780,7 +2815,7 @@ "path": "specifications/TwoPhase", "title": "Two-Phase Handshaking", "description": "Specification of a very simple hardware protocol called two-phase handshaking", - "source": "", + "sources": [], "authors": [ "Leslie Lamport", "Stephan Merz" @@ -2834,7 +2869,7 @@ "path": "specifications/aba-asyn-byz", "title": "Asynchronous Byzantine Consensus", "description": "Spec of the protocol from paper *Asynchronous Consensus and Broadcast Protocols* (1985)", - "source": "", + "sources": [], "authors": [ "Igor Konnov", "Josef Widder", @@ -2862,11 +2897,97 @@ } ] }, + { + "path": "specifications/acp", + "title": "Atomic Commitment Protocol", + "description": "TLA⁺ specs from CS-986: Advanced Topics in Program Verification", + "sources": [ + "https://members.loria.fr/SMerz/talks/argentina2005/Charpentier/charpov/Teaching/CS-986/TLC/", + "https://dl.acm.org/doi/10.5555/302430.302436" + ], + "authors": [ + "Stephan Merz" + ], + "tags": [], + "modules": [ + { + "path": "specifications/acp/ACP_NB.tla", + "communityDependencies": [], + "tlaLanguageVersion": 2, + "features": [], + "models": [] + }, + { + "path": "specifications/acp/ACP_NB_TLC.tla", + "communityDependencies": [], + "tlaLanguageVersion": 2, + "features": [], + "models": [ + { + "path": "specifications/acp/ACP_NB_TLC.cfg", + "runtime": "00:00:01", + "size": "small", + "mode": "exhaustive search", + "features": [ + "ignore deadlock", + "liveness" + ], + "result": "success" + } + ] + }, + { + "path": "specifications/acp/ACP_NB_WRONG_TLC.tla", + "communityDependencies": [], + "tlaLanguageVersion": 2, + "features": [], + "models": [ + { + "path": "specifications/acp/ACP_NB_WRONG_TLC.cfg", + "runtime": "00:00:01", + "size": "small", + "mode": "exhaustive search", + "features": [ + "ignore deadlock", + "liveness" + ], + "result": "safety failure" + } + ] + }, + { + "path": "specifications/acp/ACP_SB.tla", + "communityDependencies": [], + "tlaLanguageVersion": 2, + "features": [], + "models": [] + }, + { + "path": "specifications/acp/ACP_SB_TLC.tla", + "communityDependencies": [], + "tlaLanguageVersion": 2, + "features": [], + "models": [ + { + "path": "specifications/acp/ACP_SB_TLC.cfg", + "runtime": "00:00:01", + "size": "small", + "mode": "exhaustive search", + "features": [ + "ignore deadlock", + "liveness" + ], + "result": "success" + } + ] + } + ] + }, { "path": "specifications/allocator", "title": "Resource Allocator", "description": "Specification of a resource allocator", - "source": "", + "sources": [], "authors": [ "Stephan Merz" ], @@ -2950,7 +3071,7 @@ "path": "specifications/bcastByz", "title": "Asynchronous Reliable Broadcast", "description": "Algorithm from paper *Simulating authenticated broadcasts to derive simple fault-tolerant algorithms* (1987)", - "source": "", + "sources": [], "authors": [ "Igor Konnov", "Josef Widder", @@ -2994,7 +3115,7 @@ "path": "specifications/bcastFolklore", "title": "Folklore Reliable Broadcast", "description": "Algorithm from paper *Unreliable failure detectors for reliable distributed systems* (1996)", - "source": "", + "sources": [], "authors": [ "Igor Konnov", "Josef Widder", @@ -3026,7 +3147,7 @@ "path": "specifications/bosco", "title": "The Bosco Byzantine Consensus Algorithm", "description": "Algorithm from paper *Bosco: One-step byzantine asynchronous consensus* (2008)", - "source": "", + "sources": [], "authors": [ "Igor Konnov", "Josef Widder", @@ -3052,11 +3173,216 @@ } ] }, + { + "path": "specifications/byihive", + "title": "RFC 3506: Voucher Transaction System", + "description": "Formal specification of a peer-to-peer ledger protocol", + "sources": [ + "https://github.com/byisystems/byihive" + ], + "authors": [ + "Santhosh Raju", + "Cherry G. Mathew", + "Fransisca Andriani" + ], + "tags": [], + "modules": [ + { + "path": "specifications/byihive/VoucherCancel.tla", + "communityDependencies": [], + "tlaLanguageVersion": 2, + "features": [], + "models": [ + { + "path": "specifications/byihive/VoucherCancel.cfg", + "runtime": "00:00:01", + "size": "small", + "mode": "exhaustive search", + "features": [ + "ignore deadlock" + ], + "result": "success" + } + ] + }, + { + "path": "specifications/byihive/VoucherIssue.tla", + "communityDependencies": [], + "tlaLanguageVersion": 2, + "features": [], + "models": [ + { + "path": "specifications/byihive/VoucherIssue.cfg", + "runtime": "00:00:01", + "size": "small", + "mode": "exhaustive search", + "features": [ + "ignore deadlock", + "liveness" + ], + "result": "success" + } + ] + }, + { + "path": "specifications/byihive/VoucherLifeCycle.tla", + "communityDependencies": [], + "tlaLanguageVersion": 2, + "features": [], + "models": [ + { + "path": "specifications/byihive/VoucherLifeCycle.cfg", + "runtime": "00:00:01", + "size": "small", + "mode": "exhaustive search", + "features": [ + "ignore deadlock" + ], + "result": "success" + } + ] + }, + { + "path": "specifications/byihive/VoucherRedeem.tla", + "communityDependencies": [], + "tlaLanguageVersion": 2, + "features": [], + "models": [ + { + "path": "specifications/byihive/VoucherRedeem.cfg", + "runtime": "00:00:01", + "size": "small", + "mode": "exhaustive search", + "features": [ + "ignore deadlock" + ], + "result": "success" + } + ] + }, + { + "path": "specifications/byihive/VoucherTransfer.tla", + "communityDependencies": [], + "tlaLanguageVersion": 2, + "features": [], + "models": [ + { + "path": "specifications/byihive/VoucherTransfer.cfg", + "runtime": "00:00:01", + "size": "small", + "mode": "exhaustive search", + "features": [ + "ignore deadlock" + ], + "result": "success" + } + ] + } + ] + }, + { + "path": "specifications/byzpaxos", + "title": "Byzantizing Paxos by Refinement", + "description": "Mechanically-checked safety proof of a Byzantine Paxos algorithm", + "sources": [ + "http://lamport.azurewebsites.net/pubs/pubs.html#web-byzpaxos", + "http://lamport.azurewebsites.net/pubs/web-byzpaxos.pdf", + "http://lamport.azurewebsites.net/tla/byzpaxos.html" + ], + "authors": [ + "Leslie Lamport" + ], + "tags": [], + "modules": [ + { + "path": "specifications/byzpaxos/BPConProof.tla", + "communityDependencies": [], + "tlaLanguageVersion": 2, + "features": [ + "pluscal", + "proof" + ], + "models": [ + { + "path": "specifications/byzpaxos/BPConProof.cfg", + "runtime": "unknown", + "size": "large", + "mode": "exhaustive search", + "features": [], + "result": "unknown" + } + ] + }, + { + "path": "specifications/byzpaxos/Consensus.tla", + "communityDependencies": [], + "tlaLanguageVersion": 2, + "features": [ + "pluscal", + "proof" + ], + "models": [ + { + "path": "specifications/byzpaxos/Consensus.cfg", + "runtime": "00:00:01", + "size": "small", + "mode": "exhaustive search", + "features": [ + "ignore deadlock", + "liveness" + ], + "result": "success" + } + ] + }, + { + "path": "specifications/byzpaxos/PConProof.tla", + "communityDependencies": [], + "tlaLanguageVersion": 2, + "features": [ + "pluscal", + "proof" + ], + "models": [ + { + "path": "specifications/byzpaxos/PConProof.cfg", + "runtime": "00:06:00", + "size": "large", + "mode": "exhaustive search", + "features": [], + "result": "success" + } + ] + }, + { + "path": "specifications/byzpaxos/VoteProof.tla", + "communityDependencies": [], + "tlaLanguageVersion": 2, + "features": [ + "pluscal", + "proof" + ], + "models": [ + { + "path": "specifications/byzpaxos/VoteProof.cfg", + "runtime": "00:00:05", + "size": "small", + "mode": "exhaustive search", + "features": [ + "ignore deadlock", + "liveness" + ], + "result": "success" + } + ] + } + ] + }, { "path": "specifications/c1cs", "title": "Consensus in One Communication Step", "description": "Algorithm from paper *Consensus in one communication step* (2001)", - "source": "", + "sources": [], "authors": [ "Igor Konnov", "Josef Widder", @@ -3088,7 +3414,7 @@ "path": "specifications/cbc_max", "title": "Condition-Based Consensus", "description": "Algorithm from paper *Evaluating the condition-based approach to solve consensus* (2003)", - "source": "", + "sources": [], "authors": [ "Igor Konnov", "Josef Widder", @@ -3109,7 +3435,7 @@ "path": "specifications/cf1s-folklore", "title": "One-Step Consensus with Zero-Degradation", "description": "Algorithm from paper *One-step Consensus with Zero-Degradation* (2006)", - "source": "", + "sources": [], "authors": [ "Igor Konnov", "Josef Widder", @@ -3141,7 +3467,7 @@ "path": "specifications/chang_roberts", "title": "Chang-Roberts Algorithm for Leader Election in a Ring", "description": "Algorithm from paper *An improved algorithm for decentralized extrema-finding in circular configurations of processes* (1979)", - "source": "", + "sources": [], "authors": [ "Stephan Merz" ], @@ -3181,7 +3507,7 @@ "path": "specifications/detector_chan96", "title": "Failure Detector", "description": "Algorithm from paper *Unreliable failure detectors for reliable distributed systems* (1996)", - "source": "", + "sources": [], "authors": [ "Igor Konnov", "Josef Widder", @@ -3227,7 +3553,7 @@ "path": "specifications/dijkstra-mutex", "title": "Dijkstra's Mutual Exclusion Algorithm", "description": "Algorithm from paper *Solution of a Problem in Concurrent Programming Control* (1965)", - "source": "", + "sources": [], "authors": [ "Leslie Lamport" ], @@ -3302,7 +3628,9 @@ "path": "specifications/diskpaxos", "title": "SWMR Shared Memory Disk Paxos", "description": "A formalization of the SWMR-Shared-Memory Disk Paxos", - "source": "https://github.com/nano-o/MultiPaxos/blob/master/DiskPaxos.tla", + "sources": [ + "https://github.com/nano-o/MultiPaxos/blob/master/DiskPaxos.tla" + ], "authors": [ "Giuliano Losa", "Leslie Lamport" @@ -3352,7 +3680,7 @@ "path": "specifications/echo", "title": "The Echo Algorithm", "description": "Algorithm for constructing a spanning tree in an undirected graph", - "source": "", + "sources": [], "authors": [ "Stephan Merz" ], @@ -3396,7 +3724,7 @@ "path": "specifications/ewd426", "title": "EWD 426: Token Stabilization", "description": "Dijkstra's classical stabilizing token ring algorithm, EWD426", - "source": "", + "sources": [], "authors": [ "Markus Kuppe", "Murat Demirbas" @@ -3453,7 +3781,7 @@ "path": "specifications/ewd687a", "title": "EWD687a: Detecting Termination in Distributed Computations", "description": "Dijkstra's Termination Detection Algorithm", - "source": "", + "sources": [], "authors": [ "Leslie Lamport", "Markus Kuppe", @@ -3534,7 +3862,9 @@ "path": "specifications/ewd840", "title": "EWD840: Termination Detection in a Ring", "description": "EWD840: Termination Detection in a Ring", - "source": "Dijkstra's termination detection algorithm for ring topologies", + "sources": [ + "Dijkstra's termination detection algorithm for ring topologies" + ], "authors": [ "Stephan Merz" ], @@ -3645,7 +3975,7 @@ "path": "specifications/ewd998", "title": "EWD998: Termination Detection in a Ring with Asynchronous Message Delivery", "description": "Variant of EWD 840 given by Shmuel Safra", - "source": "", + "sources": [], "authors": [ "Markus Kuppe", "Stephan Merz" @@ -3906,7 +4236,7 @@ "path": "specifications/glowingRaccoon", "title": "PCR Testing for Snippets of DNA", "description": "Specification of a PCR test process", - "source": "", + "sources": [], "authors": [ "Martin Harrison" ], @@ -3974,7 +4304,7 @@ "path": "specifications/lamport_mutex", "title": "Distributed Mutual Exclusion", "description": "Specification of Lamport's distributed mutual exclusion algorithm", - "source": "", + "sources": [], "authors": [ "Stephan Merz" ], @@ -4057,7 +4387,7 @@ "path": "specifications/nbacc_ray97", "title": "Asynchronous Non-Blocking Atomic Commit", "description": "Algorithm from paper *A case study of agreement problems in distributed systems: non-blocking atomic commitment* (1997)", - "source": "", + "sources": [], "authors": [ "Igor Konnov", "Josef Widder", @@ -4087,7 +4417,7 @@ "path": "specifications/nbacg_guer01", "title": "Asynchronous Non-Blocking Atomic Commitment with Failure Detectors", "description": "Algorithm from paper *On the hardness of failure-sensitive agreement problems* (2001)", - "source": "", + "sources": [], "authors": [ "Igor Konnov", "Josef Widder", @@ -4119,7 +4449,7 @@ "path": "specifications/spanning", "title": "Spanning Tree Broadcast Algorithm", "description": "A spanning tree broadcast algorithm", - "source": "", + "sources": [], "authors": [ "Igor Konnov", "Josef Widder", @@ -4156,7 +4486,7 @@ "path": "specifications/sums_even", "title": "Proof x+x is Even", "description": "Two proofs that x+x is even for every natural number x", - "source": "", + "sources": [], "authors": [ "Stephan Merz" ], @@ -4204,7 +4534,7 @@ "path": "specifications/tower_of_hanoi", "title": "The Tower of Hanoi Puzzle", "description": "Famous puzzle involving stacking disks on towers", - "source": "", + "sources": [], "authors": [ "Alexander Niederbühl", "Markus Kuppe" @@ -4270,13 +4600,36 @@ "path": "specifications/transaction_commit", "title": "Transaction Commit Models", "description": "Ordinary transaction commit, two-phase commit, and Paxos commit", - "source": "http://research.microsoft.com/en-us/um/people/lamport/pubs/pubs.html#paxos-commit", + "sources": [ + "http://research.microsoft.com/en-us/um/people/lamport/pubs/pubs.html#paxos-commit", + "https://www.microsoft.com/en-us/research/uploads/prod/2004/01/consensus-on-transaction-commit.pdf", + "https://github.com/muratdem/PlusCal-examples/tree/master/2PCTM" + ], "authors": [ "Jim Gray", - "Leslie Lamport" + "Leslie Lamport", + "Murat Demirbas" ], "tags": [], "modules": [ + { + "path": "specifications/transaction_commit/2PCwithBTM.tla", + "communityDependencies": [], + "tlaLanguageVersion": 2, + "features": [ + "pluscal" + ], + "models": [ + { + "path": "specifications/transaction_commit/2PCwithBTM.cfg", + "runtime": "00:00:01", + "size": "small", + "mode": "exhaustive search", + "features": [], + "result": "success" + } + ] + }, { "path": "specifications/transaction_commit/PaxosCommit.tla", "communityDependencies": [], diff --git a/specifications/2PCwithBTM/README.md b/specifications/2PCwithBTM/README.md deleted file mode 100644 index f5791e90..00000000 --- a/specifications/2PCwithBTM/README.md +++ /dev/null @@ -1,10 +0,0 @@ -#### 2PCwithBTM -- Specification's authors: Murat Demirbas -- Original paper: Gray, Jim, and Leslie Lamport. Consensus on transaction commit. ACM Transactions on Database Systems (TODS) 31.1 (2006): 133-160. -- Extended modules: FinSet, Int, Seq -- Computation models: no faults -- Some properties checked with TLC: consistency, NotCommitted -- TLA+ files - - - diff --git a/specifications/802.16/README.md b/specifications/802.16/README.md deleted file mode 100644 index 5f723c44..00000000 --- a/specifications/802.16/README.md +++ /dev/null @@ -1,9 +0,0 @@ -#### 802.16 -- Specification's authors: Prasad Narayana, Ruiming Chen, Yao Zhao, Yan Chen, Zhi (Judy) Fu, and Hai Zhou -- Original paper: 802.16-2009 - IEEE Standard for Local and metropolitan area networks Part 16: Air Interface for Broadband Wireless Access Systems -- Extended modules: Int, Seq, FinSet -- Computation models: no faults -- Some properties checked with TLC: DoS vulnerability -- TLA+ files - - diff --git a/specifications/acp-nb-wrong/README.md b/specifications/acp-nb-wrong/README.md deleted file mode 100644 index ee49fe0c..00000000 --- a/specifications/acp-nb-wrong/README.md +++ /dev/null @@ -1,7 +0,0 @@ -#### acp-nb-wrong -- Specification's authors: Stephan Merz -- Original paper: Babaoğlu, Özalp, and Sam Toueg. Non-blocking atomic commitment. Distributed systems (2nd Ed.). ACM Press/Addison-Wesley Publishing Co., 1993. -- Extended modules: default theories -- Computation models: clean crashes -- Some properties checked with TLC: AC1-AC5, termination, AllAbort, AllCommit -- TLA+ files \ No newline at end of file diff --git a/specifications/acp-nb/README.md b/specifications/acp-nb/README.md deleted file mode 100644 index fd7c9292..00000000 --- a/specifications/acp-nb/README.md +++ /dev/null @@ -1,7 +0,0 @@ -#### acp-nb -- Specification's authors: Stephan Merz -- Original paper: Babaoğlu, Özalp, and Sam Toueg. Non-blocking atomic commitment. Distributed systems (2nd Ed.). ACM Press/Addison-Wesley Publishing Co., 1993. -- Extended modules: default theories -- Computation models: clean crashes -- Some properties checked with TLC: AC1-AC5, termination, AllAbort, AllCommit -- TLA+ files \ No newline at end of file diff --git a/specifications/acp-sb/README.md b/specifications/acp-sb/README.md deleted file mode 100644 index 7588ac93..00000000 --- a/specifications/acp-sb/README.md +++ /dev/null @@ -1,7 +0,0 @@ -#### acp-sb -- Specification's authors: Stephan Merz -- Original paper: Babaoğlu, Özalp, and Sam Toueg. Non-blocking atomic commitment. Distributed systems (2nd Ed.). ACM Press/Addison-Wesley Publishing Co., 1993. -- Extended modules: default theories -- Computation models: clean crashes -- Some properties checked with TLC: AC1-AC5, no recovery, termincation -- TLA+ files \ No newline at end of file diff --git a/specifications/acp/ACP_NB.tla b/specifications/acp/ACP_NB.tla new file mode 100644 index 00000000..01543bce --- /dev/null +++ b/specifications/acp/ACP_NB.tla @@ -0,0 +1,177 @@ +-------------------------------- MODULE ACP_NB --------------------------------- +\* Time-stamp: <10 Jun 2002 at 14:06:57 by charpov on berlioz.cs.unh.edu> + +\* Non blocking Atomic Committment Protocol (ACP-NB) +\* The non blocking property AC5 is obtained by using a reliable broadcast +\* implemented as follows: +\* - upon reception of a broadcast message, this message is forwarded to all +\* participants before it's delivered to the local site; +\* - since participant i does not forward to itself, forward[i] is used to +\* store the decision before it's delivered (and becomes "decision") + +EXTENDS ACP_SB + +-------------------------------------------------------------------------------- + +\* Participants type is extended with a "forward" variable. +\* Coordinator type is unchanged. + +TypeInvParticipantNB == participant \in [ + participants -> [ + vote : {yes, no}, + alive : BOOLEAN, + decision : {undecided, commit, abort}, + faulty : BOOLEAN, + voteSent : BOOLEAN, + forward : [ participants -> {notsent, commit, abort} ] + ] + ] + +TypeInvNB == TypeInvParticipantNB /\ TypeInvCoordinator + +-------------------------------------------------------------------------------- + +\* Initially, participants have not forwarded anything yet + +InitParticipantNB == participant \in [ + participants -> [ + vote : {yes, no}, + alive : {TRUE}, + decision : {undecided}, + faulty : {FALSE}, + voteSent : {FALSE}, + forward : [ participants -> {notsent} ] + ] + ] + +InitNB == InitParticipantNB /\ InitCoordinator + +-------------------------------------------------------------------------------- + +\* Participant statements that realize a better broadcast + +\* forward(i,j): forwarding of the predecision from participant i to participant j +\* IF +\* particpant i is alive +\* participant i has received a decision (stored in forward[i]) +\* participant i has not yet forwarded this decision to participant j +\* THEN +\* participant i forwards the decision to participant j + +forward(i,j) == /\ i # j + /\ participant[i].alive + /\ participant[i].forward[i] # notsent + /\ participant[i].forward[j] = notsent + /\ participant' = [participant EXCEPT ![i] = + [@ EXCEPT !.forward = + [@ EXCEPT ![j] = participant[i].forward[i]] + ] + ] + /\ UNCHANGED<> + + +\* preDecideOnForward(i,j): participant i receives decision from participant j +\* IF +\* participant i is alive +\* participant i has yet to receive a decision +\* participant j has forwarded its decision to participant i +\* THEN +\* participant i (pre)decides in accordance with participant j's decision + +preDecideOnForward(i,j) == /\ i # j + /\ participant[i].alive + /\ participant[i].forward[i] = notsent + /\ participant[j].forward[i] # notsent + /\ participant' = [participant EXCEPT ![i] = + [@ EXCEPT !.forward = + [@ EXCEPT ![i] = participant[j].forward[i]] + ] + ] + /\ UNCHANGED<> + + +\* preDecide(i): participant i receives decision from coordinator +\* IF +\* participant i is alive +\* participant i has yet to receive a decision +\* coordinator has sent its decision to participant i +\* THEN +\* participant i (pre)decides in accordance with coordinator's decision + +preDecide(i) == /\ participant[i].alive + /\ participant[i].forward[i] = notsent + /\ coordinator.broadcast[i] # notsent + /\ participant' = [participant EXCEPT ![i] = + [@ EXCEPT !.forward = + [@ EXCEPT ![i] = coordinator.broadcast[i]] + ] + ] + /\ UNCHANGED<> + + +\* decideNB(i): Actual decision, after predecision has been forwarded +\* IF +\* participant i is alive +\* participant i has forwarded its (pre)decision to all other participants +\* THEN +\* participant i decides in accordance with it's predecision + +decideNB(i) == /\ participant[i].alive + /\ \A j \in participants : participant[i].forward[j] # notsent + /\ participant' = [participant EXCEPT ![i] = + [@ EXCEPT !.decision = participant[i].forward[i]] + ] + /\ UNCHANGED<> + + +\* abortOnTimeout(i): conditions for a timeout are simulated +\* IF +\* participant is alive and undecided and coordinator is not alive +\* coordinator died before sending decision to all participants who are alive +\* all dead participants died before forwarding decision to a participant who is alive +\* THEN +\* decide abort + +abortOnTimeout(i) == /\ participant[i].alive + /\ participant[i].decision = undecided + /\ ~coordinator.alive + /\ \A j \in participants : participant[j].alive => coordinator.broadcast[j] = notsent + /\ \A j,k \in participants : ~participant[j].alive /\ participant[k].alive => participant[j].forward[k] = notsent + /\ participant' = [participant EXCEPT ![i] = [@ EXCEPT !.decision = abort]] + /\ UNCHANGED<> + +--------------------------------------------------------------------------------- + +\* FOR N PARTICIPANTS + +parProgNB(i,j) == \/ sendVote(i) + \/ abortOnVote(i) + \/ abortOnTimeoutRequest(i) + \/ forward(i,j) + \/ preDecideOnForward(i,j) + \/ abortOnTimeout(i) + \/ preDecide(i) + \/ decideNB(i) + +parProgNNB == \E i,j \in participants : parDie(i) \/ parProgNB(i,j) + +progNNB == parProgNNB \/ coordProgN + +fairnessNB == /\ \A i \in participants : WF_<>(\E j \in participants : parProgNB(i,j)) + /\ WF_<>(coordProgB) + +SpecNB == InitNB /\ [][progNNB]_<> /\ fairnessNB + +-------------------------------------------------------------------------------- + +\* (SOME) INVALID PROPERTIES + +AllCommit == \A i \in participants : <>(participant[i].decision = commit \/ participant[i].faulty) + +AllAbort == \A i \in participants : <>(participant[i].decision = abort \/ participant[i].faulty) + +AllCommitYesVotes == \A i \in participants : + \A j \in participants : participant[j].vote = yes + ~> participant[i].decision = commit \/ participant[i].faulty \/ coordinator.faulty + +================================================================================ diff --git a/specifications/acp/ACP_NB_TLC.cfg b/specifications/acp/ACP_NB_TLC.cfg new file mode 100644 index 00000000..4e09b6e9 --- /dev/null +++ b/specifications/acp/ACP_NB_TLC.cfg @@ -0,0 +1,36 @@ +\* TLC configuration file for ACP_NB +\* Safety has been checked for 3 participants (without symmetries) +\* and 4 participants (with symmetries) +\* Liveness has been checked for 3 participants +\* Incorrect safety and liveness properties are both detected +\* (with meaningful counterexamples) with 2 participants + +\*SYMMETRY +\* Perms + +CONSTANTS + participants = { p0, p1 } + + waiting = waiting + notsent = notsent + undecided = undecided + commit = commit + abort = abort + yes = yes + no = no + +SPECIFICATION + SpecNB + +PROPERTIES + AC1 AC2 AC3_1 AC4_alt \* safety + AC3_2 AC5 \* liveness + +\* DecisionReachedNoFault \* invalid, to check that TLC does its job +\* AbortImpliesNoVote \* invalid, to check that TLC does its job +\* StrongerAC3_1 \* invalid, to check that TLC does its job +\* AllCommit \* invalid, to check that TLC does its job +\* AllAbort \* invalid, to check that TLC does its job + +CHECK_DEADLOCK FALSE + diff --git a/specifications/acp/ACP_NB_TLC.tla b/specifications/acp/ACP_NB_TLC.tla new file mode 100644 index 00000000..480e2c9f --- /dev/null +++ b/specifications/acp/ACP_NB_TLC.tla @@ -0,0 +1,18 @@ +------------------------------ MODULE ACP_NB_TLC ------------------------------- + +\* ACP_SB, TLC ready + +EXTENDS ACP_NB, TLC + +Perms == Permutations(participants) \* to benefit from symmetries + +-------------------------------------------------------------------------------- + +\* AC4 rewritten in a way that is better handled by TLC + +AC4_alt == [][ /\ (\A i \in participants : participant[i].decision = commit + => (participant'[i].decision = commit)) + /\ (\A j \in participants : participant[j].decision = abort + => (participant'[j].decision = abort))]_<> + +================================================================================ diff --git a/specifications/acp/ACP_NB_WRONG_TLC.cfg b/specifications/acp/ACP_NB_WRONG_TLC.cfg new file mode 100644 index 00000000..aec43a26 --- /dev/null +++ b/specifications/acp/ACP_NB_WRONG_TLC.cfg @@ -0,0 +1,23 @@ +\* TLC configuration file for ACP_NB_WRONG +\* The fundamental consistency property AC1 does not hold + +CONSTANTS + participants = { p0, p1 } + + waiting = waiting + notsent = notsent + undecided = undecided + commit = commit + abort = abort + yes = yes + timeout = timeout + no = no + +SPECIFICATION + SpecNB + +PROPERTIES + AC1 \* invalid, TLC found that! + +CHECK_DEADLOCK FALSE + diff --git a/specifications/acp/ACP_NB_WRONG_TLC.tla b/specifications/acp/ACP_NB_WRONG_TLC.tla new file mode 100644 index 00000000..d4e77f91 --- /dev/null +++ b/specifications/acp/ACP_NB_WRONG_TLC.tla @@ -0,0 +1,121 @@ +--------------------------- MODULE ACP_NB_WRONG_TLC ---------------------------- + +\* Erroneous Non blocking Atomic Committment Protocol (ACP-NB) +\* The mistake is to deliver a broacast message locally *before* it has been +\* forwarded to other participants. +\* This protocol does not satisfy the consistency property AC1 + +EXTENDS ACP_SB + +-------------------------------------------------------------------------------- + +\* Participants type is extended with a "forward" variable. +\* Coordinator type is unchanged. + +TypeInvParticipantNB == participant \in [ + participants -> [ + vote : {yes, no}, + alive : BOOLEAN, + decision : {undecided, commit, abort}, + faulty : BOOLEAN, + voteSent : BOOLEAN, + forward : [ participants -> {notsent, commit, abort} ] + ] + ] + +TypeInvNB == TypeInvParticipantNB /\ TypeInvCoordinator + +-------------------------------------------------------------------------------- + +\* Initially, participants have not forwarded anything yet + +InitParticipantNB == participant \in [ + participants -> [ + vote : {yes, no}, + alive : {TRUE}, + decision : {undecided}, + faulty : {FALSE}, + voteSent : {FALSE}, + forward : [ participants -> {notsent} ] + ] + ] + +InitNB == InitParticipantNB /\ InitCoordinator + +-------------------------------------------------------------------------------- + +\* Participant statements that realize a better broadcast + +\* forward(i,j): forwarding of the predecision from participant i to participant j +\* IF +\* particpant i is alive +\* participant i has received a decision and has decided (it shouldn't have decided yet) +\* participant i has not yet forwarded this decision to participant j +\* THEN +\* participant i forwards the decision to participant j + +forward(i,j) == /\ i # j + /\ participant[i].alive + /\ participant[i].decision # notsent + /\ participant[i].forward[j] = notsent + /\ participant' = [participant EXCEPT ![i] = + [@ EXCEPT !.forward = + [@ EXCEPT ![j] = participant[i].decision] + ] + ] + /\ UNCHANGED<> + + +\* decideOnForward(i,j): participant i receives decision from participant j +\* IF +\* participant i is alive +\* participant i has yet to receive a decision +\* participant j has forwarded its decision to participant i +\* THEN +\* participant i decides in accordance with participant j's decision (it should only predecide) + +decideOnForward(i,j) == /\ i # j + /\ participant[i].alive + /\ participant[i].decision = undecided + /\ participant[j].forward[i] # notsent + /\ participant' = [participant EXCEPT ![i] = + [@ EXCEPT !.decision = participant[j].forward[i]] + ] + /\ UNCHANGED<> + + +\* abortOnTimeout(i): conditions for a timeout are simulated +\* IF +\* participant is alive and undecided and coordinator is not alive +\* coordinator died before sending decision to all participants who are alive +\* all dead participants died before forwarding decision to a participant who is alive +\* THEN +\* decide abort + +abortOnTimeout(i) == /\ participant[i].alive + /\ participant[i].decision = undecided + /\ ~coordinator.alive + /\ \A j \in participants : participant[j].alive => coordinator.broadcast[j] = notsent + /\ \A j,k \in participants : ~participant[j].alive /\ participant[k].alive => participant[j].forward[k] = notsent + /\ participant' = [participant EXCEPT ![i] = [@ EXCEPT !.decision = abort]] + /\ UNCHANGED<> + +--------------------------------------------------------------------------------- + +\* FOR N PARTICIPANTS + +parProgNB(i,j) == \/ parProg(i) + \/ forward(i,j) + \/ decideOnForward(i,j) + \/ abortOnTimeout(i) + +parProgNNB == \E i,j \in participants : parDie(i) \/ parProgNB(i,j) + +progNNB == parProgNNB \/ coordProgN + +fairnessNB == /\ \A i \in participants : WF_<>(\E j \in participants : parProgNB(i,j)) + /\ WF_<>(coordProgB) + +SpecNB == InitNB /\ [][progNNB]_<> /\ fairnessNB + +================================================================================ diff --git a/specifications/acp/ACP_SB.tla b/specifications/acp/ACP_SB.tla new file mode 100644 index 00000000..6f35067a --- /dev/null +++ b/specifications/acp/ACP_SB.tla @@ -0,0 +1,402 @@ +--------------------------------- MODULE ACP_SB -------------------------------- +\* Time-stamp: <10 Jun 2002 at 12:39:50 by charpov on berlioz.cs.unh.edu> + +\* `^Atomic Committment Protocol^' with Simple Broadcast primitive (ACP-SB) +\* From: +\* `^Sape Mullender^', editor. Distributed Systems. +\* Chapter 6: Non-Blocking Atomic Commitment, by `^\"O. Babao\u{g}lu and S. Toueg.^' +\* 1993. + +\******************************************************************************* +\* Synchronous communication has been replaced with (implicit) asynchronous communication. +\* Failures are detected "magically" instead or relying on timeouts. +\* +\* This version of the protocol uses a "simple broadcast" where a broadcast is simply a +\* series of messages sent, possibly interrupted by a failure. Consequently, this algorithm +\* is "non terminating" and property AC5 does not hold. +\******************************************************************************* + +CONSTANTS + participants, \* set of participants + yes, no, \* vote + undecided, commit, abort, \* decision + waiting, \* coordinator state wrt a participant + notsent \* broadcast state wrt a participant + +VARIABLES + participant, \* participants (N) + coordinator \* coordinator (1) + +-------------------------------------------------------------------------------- + +TypeInvParticipant == participant \in [ + participants -> [ + vote : {yes, no}, + alive : BOOLEAN, + decision : {undecided, commit, abort}, + faulty : BOOLEAN, + voteSent : BOOLEAN + ] + ] + +TypeInvCoordinator == coordinator \in [ + request : [participants -> BOOLEAN], + vote : [participants -> {waiting, yes, no}], + broadcast : [participants -> {commit, abort, notsent}], + decision : {commit, abort, undecided}, + alive : BOOLEAN, + faulty : BOOLEAN + ] + +TypeInv == TypeInvParticipant /\ TypeInvCoordinator + +-------------------------------------------------------------------------------- + +\* Initially: +\* All the participants: +\* have a yes/no vote +\* are alive and not faulty +\* have not sent in their votes yet +\* are undecided about final decision + +\* The coordinator: +\* has not sent vote requests yet +\* has not recieved votes from any participant +\* is alive and not faulty +\* has not sent broadcast messages to any participant +\* is undecided about final decision + +InitParticipant == participant \in [ + participants -> [ + vote : {yes, no}, + alive : {TRUE}, + decision : {undecided}, + faulty : {FALSE}, + voteSent : {FALSE} + ] + ] + +InitCoordinator == coordinator \in [ + request : [participants -> {FALSE}], + vote : [participants -> {waiting}], + alive : {TRUE}, + broadcast : [participants -> {notsent}], + decision : {undecided}, + faulty : {FALSE} + ] + +Init == InitParticipant /\ InitCoordinator + +-------------------------------------------------------------------------------- +\* COORDINATOR STATEMENTS + +\* request(i): +\* IF +\* coordinator is alive +\* request for vote has not been sent to participant i +\* THEN `~ why isn't THEN left-justified? ~' +\* request for vote is sent to participant i + +request(i) == /\ coordinator.alive + /\ ~coordinator.request[i] + /\ coordinator' = [coordinator EXCEPT !.request = + [@ EXCEPT ![i] = TRUE] + ] + /\ UNCHANGED<> + + +\* getVote(i): +\* IF +\* coordinator is alive +\* coordinator is still undecided +\* coordinator has sent request for votes to all participants +\* coordinator is waiting to receive a vote from participant i +\* participant i has sent the vote message +\* THEN +\* the coordinator can record the vote of participant i + +getVote(i) == /\ coordinator.alive + /\ coordinator.decision = undecided + /\ \A j \in participants : coordinator.request[j] + /\ coordinator.vote[i] = waiting + /\ participant[i].voteSent + /\ coordinator' = [coordinator EXCEPT !.vote = + [@ EXCEPT ![i] = participant[i].vote] + ] + /\ UNCHANGED<> + + +\* detectFault(i): +\* IF +\* coordinator is alive +\* coordinator is still undecided +\* coordinator has sent request for votes to all participants +\* coordinator is waiting for vote from participant i +\* participant i has died without sending its vote +\* THEN +\* coordinator times out on participant i and decides to abort + +detectFault(i) == /\ coordinator.alive + /\ coordinator.decision = undecided + /\ \A j \in participants : coordinator.request[j] + /\ coordinator.vote[i] = waiting + /\ ~participant[i].alive + /\ ~participant[i].voteSent + /\ coordinator' = [coordinator EXCEPT !.decision = abort] + /\ UNCHANGED<> + + +\* makeDecision: +\* IF +\* coordinator is alive +\* coordinator is undecided +\* coordinator has received votes from all participants +\* THEN +\* IF +\* all votes are yes +\* THEN +\* coordinator decides commit +\* ELSE +\* coordinator decides abort + +makeDecision == /\ coordinator.alive + /\ coordinator.decision = undecided + /\ \A j \in participants : coordinator.vote[j] \in {yes, no} + /\ \/ /\ \A j \in participants : coordinator.vote[j] = yes + /\ coordinator' = [coordinator EXCEPT !.decision = commit] + \/ /\ \E j \in participants : coordinator.vote[j] = no + /\ coordinator' = [coordinator EXCEPT !.decision = abort] + /\ UNCHANGED<> + + +\* coordBroadcast(i) (simple broadcast): +\* IF +\* coordinator is alive +\* coordinator has made a decision +\* coordinator has not sent the decision to participant i +\* THEN +\* coordinator sends its decision to participant i + +coordBroadcast(i) == /\ coordinator.alive + /\ coordinator.decision # undecided + /\ coordinator.broadcast[i] = notsent + /\ coordinator' = [coordinator EXCEPT !.broadcast = + [@ EXCEPT ![i] = coordinator.decision] + ] + /\ UNCHANGED<> + + +\* coordDie: +\* IF +\* coordinator is alive +\* THEN +\* coordinator dies +\* coordinator is now faulty + +coordDie == /\ coordinator.alive + /\ coordinator' = [coordinator EXCEPT !.alive = FALSE, !.faulty = TRUE] + /\ UNCHANGED<> + +------------------------------------------------------------------------------ + +\* PARTICIPANT STATEMENTS + +\* sendVote(i): +\* IF +\* participant is alive +\* participant has received a request for vote +\* THEN +\* participant sends vote + +sendVote(i) == /\ participant[i].alive + /\ coordinator.request[i] + /\ participant' = [participant EXCEPT ![i] = + [@ EXCEPT !.voteSent = TRUE] + ] + /\ UNCHANGED<> + + +\* abortOnVote(i): +\* IF +\* participant is alive +\* participant is undecided +\* participant has sent its vote to the coordinator +\* participant's vote is no +\* THEN +\* participant decides (unilaterally) to abort + +abortOnVote(i) == /\ participant[i].alive + /\ participant[i].decision = undecided + /\ participant[i].voteSent + /\ participant[i].vote = no + /\ participant' = [participant EXCEPT ![i] = + [@ EXCEPT !.decision = abort] + ] + /\ UNCHANGED<> + + +\* abortOnTimeoutRequest(i): +\* IF +\* participant is alive +\* participant is still undecided +\* coordinator has died without sending request for vote +\* THEN +\* participant decides (unilaterally) to abort + +abortOnTimeoutRequest(i) == /\ participant[i].alive + /\ participant[i].decision = undecided + /\ ~coordinator.alive + /\ ~coordinator.request[i] + /\ participant' = [participant EXCEPT ![i] = + [@ EXCEPT !.decision = abort] + ] + /\ UNCHANGED<> + + +\* decide(i): +\* IF +\* participant is alive +\* participant is undecided +\* participant has recieved decision from the coordinator +\* THEN +\* participant decides according to decision from coordinator +\* + +decide(i) == /\ participant[i].alive + /\ participant[i].decision = undecided + /\ coordinator.broadcast[i] # notsent + /\ participant' = [participant EXCEPT ![i] = + [@ EXCEPT !.decision = coordinator.broadcast[i]] + ] + /\ UNCHANGED<> + + +\* parDie(i): +\* IF +\* participant is alive +\* THEN +\* participant dies and is now faulty + +parDie(i) == /\ participant[i].alive + /\ participant' = [participant EXCEPT ![i] = + [@ EXCEPT !.alive = FALSE, !.faulty = TRUE] + ] + /\ UNCHANGED<> + +------------------------------------------------------------------------------- + +\* FOR N PARTICIPANTS + +parProg(i) == sendVote(i) \/ abortOnVote(i) \/ abortOnTimeoutRequest(i) \/ decide(i) + +parProgN == \E i \in participants : parDie(i) \/ parProg(i) + + +coordProgA(i) == request(i) \/ getVote(i) \/ detectFault(i) \/ coordBroadcast(i) + +coordProgB == makeDecision \/ \E i \in participants : coordProgA(i) + +coordProgN == coordDie \/ coordProgB + + +progN == parProgN \/ coordProgN + +\* Death transitions are left outside of fairness + +fairness == /\ \A i \in participants : WF_<>(parProg(i)) + /\ WF_<>(coordProgB) + + +Spec == Init /\ [][progN]_<> /\ fairness + +-------------------------------------------------------------------------------- + +\* CORRECTNESS SPECIFICATION + +\******************************************************************************* +\* This specification follows the original paper, except that AC3 is stronger: +\* It forces participants to abort if one vote at least is NO (in the absence +\* of failure). +\* +\* The specification is split between safety and liveness. +\******************************************************************************* + +-------------------------------------------------------------------------------- + +\* SAFETY + +\* All participants that decide reach the same decision +AC1 == [] \A i, j \in participants : + \/ participant[i].decision # commit + \/ participant[j].decision # abort + +\* If any participant decides commit, then all participants must have votes YES +AC2 == [] ( (\E i \in participants : participant[i].decision = commit) + => (\A j \in participants : participant[j].vote = yes)) + +\* If any participant decides abort, then: +\* at least one participant voted NO, or +\* at least one participant is faulty, or +\* coordinator is faulty +AC3_1 == [] ( (\E i \in participants : participant[i].decision = abort) + => \/ (\E j \in participants : participant[j].vote = no) + \/ (\E j \in participants : participant[j].faulty) + \/ coordinator.faulty) + +\* Each participant decides at most once +AC4 == [] /\ (\A i \in participants : participant[i].decision = commit + => [](participant[i].decision = commit)) + /\ (\A j \in participants : participant[j].decision = abort + => [](participant[j].decision = abort)) + +-------------------------------------------------------------------------------- + +\* LIVENESS +\* (stronger for AC3 than in the original paper) + +AC3_2 == <> \/ \A i \in participants : participant[i].decision \in {abort, commit} + \/ \E j \in participants : participant[j].faulty + \/ coordinator.faulty + +-------------------------------------------------------------------------------- + +\* (SOME) INTERMEDIATE PROPERTIES USED IN PROOFS + +FaultyStable == /\ \A i \in participants : [](participant[i].faulty => []participant[i].faulty) + /\ [](coordinator.faulty => [] coordinator.faulty) + +VoteStable == \A i \in participants : + \/ [](participant[i].vote = yes) + \/ [](participant[i].vote = no) + +StrongerAC2 == [] ( (\E i \in participants : participant[i].decision = commit) + => /\ (\A j \in participants : participant[j].vote = yes) + /\ coordinator.decision = commit) + +StrongerAC3_1 == [] ( (\E i \in participants : participant[i].decision = abort) + => \/ (\E j \in participants : participant[j].vote = no) + \/ /\ \E j \in participants : participant[j].faulty + /\ coordinator.decision = abort + \/ /\ coordinator.faulty + /\ coordinator.decision = undecided) + +\* (AC1 follows from StrongerAC2 /\ StrongerAC3_1) + +NoRecovery == [] /\ \A i \in participants : participant[i].alive <=> ~participant[i].faulty + /\ coordinator.alive <=> ~coordinator.faulty + +-------------------------------------------------------------------------------- + +\* (SOME) INVALID PROPERTIES + +DecisionReachedNoFault == (\A i \in participants : participant[i].alive) + ~> (\A k \in participants : participant[k].decision # undecided) + +AbortImpliesNoVote == [] ( (\E i \in participants : participant[i].decision = abort) + => (\E j \in participants : participant[j].vote = no)) + +\* The following is the termination property that this SB algorithm doesn't have +AC5 == <> \A i \in participants : \/ participant[i].decision \in {abort, commit} + \/ participant[i].faulty + +================================================================================ \ No newline at end of file diff --git a/specifications/acp/ACP_SB_TLC.cfg b/specifications/acp/ACP_SB_TLC.cfg new file mode 100644 index 00000000..057d87eb --- /dev/null +++ b/specifications/acp/ACP_SB_TLC.cfg @@ -0,0 +1,37 @@ +\* TLC configuration file for ACP_SB +\* Safety has been checked for 4 participants (without symmetries) +\* and 6 participants (with symmetries) +\* Liveness has been checked for 4 participants +\* Intermediate properties have been checked for 3 participants +\* Incorrect safety and liveness properties are both detected +\* (with meaningful counterexamples) with 2 participants + +\*SYMMETRY +\* Perms + +CONSTANTS + participants = { p0, p1, p2 } + + waiting = waiting + notsent = notsent + undecided = undecided + commit = commit + abort = abort + yes = yes + no = no + +SPECIFICATION + Spec + +PROPERTIES +\* AC1 AC2 AC3_1 AC4_alt \* safety +\* AC3_2 \* liveness + +\* FaultyStable VoteStable StrongerAC2 StrongerAC3_1 NoRecovery \* intermediate properties + +\* DecisionReachedNoFault \* invalid, to check that TLC does its job +\* AbortImpliesNoVote \* invalid, to check that TLC does its job +\* AC5 \* invalid, to check that TLC does its job + +CHECK_DEADLOCK FALSE + diff --git a/specifications/acp/ACP_SB_TLC.tla b/specifications/acp/ACP_SB_TLC.tla new file mode 100644 index 00000000..e18f7c9b --- /dev/null +++ b/specifications/acp/ACP_SB_TLC.tla @@ -0,0 +1,18 @@ +------------------------------ MODULE ACP_SB_TLC ------------------------------- + +\* ACP_SB, TLC ready + +EXTENDS ACP_SB, TLC + +Perms == Permutations(participants) \* to benefit from symmetries + +-------------------------------------------------------------------------------- + +\* AC4 rewritten in a way that is better handled by TLC + +AC4_alt == [][ /\ (\A i \in participants : participant[i].decision = commit + => (participant'[i].decision = commit)) + /\ (\A j \in participants : participant[j].decision = abort + => (participant'[j].decision = abort))]_<> + +================================================================================ diff --git a/specifications/acp/README.md b/specifications/acp/README.md new file mode 100644 index 00000000..a66770e3 --- /dev/null +++ b/specifications/acp/README.md @@ -0,0 +1,8 @@ +# ACP_SB + +Author(s): Stephan Merz & students + +Source: https://members.loria.fr/SMerz/talks/argentina2005/Charpentier/charpov/Teaching/CS-986/TLC/ + +Based on [Babaoğlu, Özalp, and Sam Toueg. Non-blocking atomic commitment. Distributed systems (2nd Ed.). ACM Press/Addison-Wesley Publishing Co., 1993](https://dl.acm.org/citation.cfm?id=302436) + diff --git a/specifications/byihive/LICENSE b/specifications/byihive/LICENSE new file mode 100644 index 00000000..261eeb9e --- /dev/null +++ b/specifications/byihive/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/specifications/byihive/README.md b/specifications/byihive/README.md index 05f08f28..38325d5e 100644 --- a/specifications/byihive/README.md +++ b/specifications/byihive/README.md @@ -1,7 +1,3 @@ -#### byihive -- Specification's authors: Santhosh Raju -- Original paper: Requirements and Design for Voucher Trading System (VTS) -- Extended modules: default theories -- Computation models: ❓ -- Some properties checked with TLC: consistency -- TLA+ files \ No newline at end of file +This contains the TLA+ specifications and models of the Voucher Transaction System (VTS) as decribed in the RFC3506. +It was sourced from https://github.com/byisystems/byihive. + diff --git a/specifications/byihive/VoucherCancel.cfg b/specifications/byihive/VoucherCancel.cfg new file mode 100644 index 00000000..9ad82d70 --- /dev/null +++ b/specifications/byihive/VoucherCancel.cfg @@ -0,0 +1,10 @@ +CONSTANTS + V = {v1, v2, v3} + H = {holder1, holder2, holder3} + I = {issuer1, issuer2, issuer3} +INVARIANTS VTPTypeOK VTPConsistent +SPECIFICATION VTPSpec +\* Refinement seems not to hold +\*PROPERTY VSpec +CHECK_DEADLOCK FALSE + diff --git a/specifications/byihive/VoucherCancel.tla b/specifications/byihive/VoucherCancel.tla new file mode 100644 index 00000000..991e7ef3 --- /dev/null +++ b/specifications/byihive/VoucherCancel.tla @@ -0,0 +1,286 @@ +\* Copyright (c) 2018, Backyard Innovations Pte. Ltd., Singapore. +\* +\* Released under the terms of the Apache License 2.0 +\* See: file LICENSE that came with this software for details. +\* +\* This file contains Intellectual Property that belongs to +\* Backyard Innovations Pte Ltd., Singapore. +\* +\* Authors: Santhosh Raju +\* Cherry G. Mathew +\* Fransisca Andriani +\* +--------------------------- MODULE VoucherCancel ---------------------------- +(***************************************************************************) +(* This specification describes the cancellation of Voucher between an *) +(* Issuer and a Holder. It is implemented over the Two-Phase Commit *) +(* protocol, in which a Voucher Transaction Provider (VTP) coordinates the *) +(* Voucher Issuers (Is) to cancel vouchers (Vs) to Voucher Holders (Hs) as *) +(* described in the VoucherLifeCycle specification module. In this *) +(* specification, Hs and Is spontaneously issue Prepared messages. We *) +(* ignore the Prepare messages that the VTP can send to the Hs and Is. *) +(* *) +(* For simplicity, we also eliminate Abort messages sent by an Hs / Is *) +(* when it decides to abort. Such a message would cause the VTP to abort *) +(* the transaction, an event represented here by the VTP spontaneously *) +(* deciding to abort. *) +(* *) +(* Note: This operation is an addendum to the operations described in RFC *) +(* 3506. This operation is not described in the RFC. *) +(***************************************************************************) +CONSTANT + V, \* The set of Vouchers + H, \* The set of Voucher Holders + I \* The set of Voucher Issuers + +VARIABLES + vState, \* vState[v] is the state of voucher v. + vlcState, \* vlcState[v] is the state of the voucher life cycle + \* machine. + hState, \* hState[h] is the state of voucher holder h. + iState, \* iState[i] is the state of voucher issuer i. + vtpState, \* The state of the voucher transaction provider. + vtpCPrepared, \* The set of Hs and Is from which the VTP has received + \* "Prepared for Voucher Cancel" messages. + msgs + (***********************************************************************) + (* In the protocol, processes communicate with one another by sending *) + (* messages. For simplicity, we represent message passing with the *) + (* variable msgs whose value is the set of all messages that have been *) + (* sent. A message is sent by adding it to the set msgs. An action *) + (* that, in an implementation, would be enabled by the receipt of a *) + (* certain message is here enabled by the presence of that message in *) + (* msgs. For simplicity, messages are never removed from msgs. This *) + (* allows a single message to be received by multiple receivers. *) + (* Receipt of the same message twice is therefore allowed; but in this *) + (* particular protocol, that's not a problem. *) + (***********************************************************************) + +Messages == + (*************************************************************************) + (* The set of all possible messages. Messages of type "Prepared" are *) + (* sent from the H indicated by the message's vh field to the VTP. *) + (* Similar "Prepared" is also sent from I indicated by message's vc *) + (* field to the VTP. Messages of type "Cancel" and "Abort" are broadcast *) + (* by the VTPs, to be received by all Hs and Is. The set msgs contains *) + (* just a single copy of such a message. *) + (*************************************************************************) + [type : {"Prepared"}, vh : H] \cup + [type : {"Prepared"}, vi : I] \cup + [type : {"Cancel", "Abort"}] + +VTPTypeOK == + (*************************************************************************) + (* The type-correctness invariant *) + (*************************************************************************) + /\ vState \in [V -> {"valid", "cancelled"}] + /\ vlcState \in [V -> {"working", "done"}] + /\ hState \in [H -> {"holding", "prepared", "cancelled", "aborted"}] + /\ iState \in [I -> {"waiting", "prepared", "cancelled", "aborted"}] + /\ vtpState \in {"init", "done"} + /\ vtpCPrepared \subseteq (H \cup I) + /\ msgs \subseteq Messages + +VTPInit == + (*************************************************************************) + (* The initial predicate. *) + (*************************************************************************) + /\ vState = [v \in V |-> "valid"] + /\ vlcState = [v \in V |-> "working"] + /\ hState = [h \in H |-> "holding"] + /\ iState = [i \in I |-> "waiting"] + /\ vtpState = "init" + /\ vtpCPrepared = {} + /\ msgs = {} +----------------------------------------------------------------------------- +(***************************************************************************) +(* We now define the actions that may be performed by the processes, first *) +(* the VTP's actions, the Hs' actions, then the Is' actions. *) +(***************************************************************************) +VTPRcvPrepared(h,i) == + (*************************************************************************) + (* The VTP receives a "Prepared" message from Voucher Holder h and the *) + (* Voucher Issuer i. We could add the additional enabling condition *) + (* h,i \notin vtpCPrepared, which disables the action if the VTP has *) + (* already received this message. But there is no need, because in that *) + (* case the action has no effect; it leaves the state unchanged. *) + (*************************************************************************) + /\ vState = [v \in V |-> "valid"] + /\ vlcState = [v \in V |-> "working"] + /\ vtpState = "init" + /\ [type |-> "Prepared", vh |-> h] \in msgs + /\ [type |-> "Prepared", vi |-> i] \in msgs + /\ vtpCPrepared' = vtpCPrepared \cup {h,i} + /\ UNCHANGED <> + +VTPCancel(v) == + (*************************************************************************) + (* The VTP Cancels the voucher; enabled iff the VTP is in its *) + (* initial state and every H and I has sent a "Prepared" message. *) + (*************************************************************************) + /\ vState[v] = "valid" + /\ vlcState[v] = "working" + /\ vtpState = "init" + /\ vtpCPrepared = H \cup I + /\ vtpState' = "done" + /\ vState' = [vState EXCEPT ![v] = "cancelled"] + /\ vlcState' = [vlcState EXCEPT ![v] = "done"] + /\ msgs' = msgs \cup {[type |-> "Cancel"]} + /\ UNCHANGED <> + +VTPAbort(v) == + (*************************************************************************) + (* The VTP spontaneously aborts the transaction. *) + (*************************************************************************) + /\ vState[v] = "valid" + /\ vlcState[v] = "working" + /\ vtpState = "init" + /\ vtpState' = "done" + /\ msgs' = msgs \cup {[type |-> "Abort"]} + /\ UNCHANGED <> + +HPrepare(h) == + (*************************************************************************) + (* Voucher holder h prepares. *) + (*************************************************************************) + /\ vState = [v \in V |-> "valid"] + /\ vlcState = [v \in V |-> "working"] + /\ hState[h] = "holding" + /\ hState' = [hState EXCEPT ![h] = "prepared"] + /\ msgs' = msgs \cup {[type |-> "Prepared", vh |-> h]} + /\ UNCHANGED <> + +HChooseToAbort(h) == + (*************************************************************************) + (* Voucher holder h spontaneously decides to abort. As noted above, h *) + (* does not send any message in our simplified spec. *) + (*************************************************************************) + /\ vState = [v \in V |-> "valid"] + /\ vlcState = [v \in V |-> "working"] + /\ hState[h] = "holding" + /\ hState' = [hState EXCEPT ![h] = "aborted"] + /\ UNCHANGED <> + +HRcvCancelMsg(h) == + (*************************************************************************) + (* Voucher holder h is told by the VTP to Cancel. *) + (*************************************************************************) + /\ vState \in [V -> {"valid", "cancelled"}] + /\ vlcState \in [V -> {"working", "done"}] + /\ hState[h] = "holding" + /\ [type |-> "Cancel"] \in msgs + /\ hState' = [hState EXCEPT ![h] = "cancelled"] + /\ UNCHANGED <> + +HRcvAbortMsg(h) == + (*************************************************************************) + (* Voucher holder h is told by the VTP to abort. *) + (*************************************************************************) + /\ vState = [v \in V |-> "valid"] + /\ vlcState = [v \in V |-> "working"] + /\ hState[h] = "holding" + /\ [type |-> "Abort"] \in msgs + /\ hState' = [hState EXCEPT ![h] = "aborted"] + /\ UNCHANGED <> + +IPrepare(i) == + (*************************************************************************) + (* Voucher issuer i prepares. *) + (*************************************************************************) + /\ vState = [v \in V |-> "valid"] + /\ vlcState = [v \in V |-> "working"] + /\ iState[i] = "waiting" + /\ iState' = [iState EXCEPT ![i] = "prepared"] + /\ msgs' = msgs \cup {[type |-> "Prepared", vi |-> i]} + /\ UNCHANGED <> + +IChooseToAbort(i) == + (*************************************************************************) + (* Voucher issuer i spontaneously decides to abort. As noted above, i *) + (* does not send any message in our simplified spec. *) + (*************************************************************************) + /\ vState = [v \in V |-> "valid"] + /\ vlcState = [v \in V |-> "working"] + /\ iState[i] = "waiting" + /\ iState' = [iState EXCEPT ![i] = "aborted"] + /\ UNCHANGED <> + +IRcvCancelMsg(i) == + (*************************************************************************) + (* Voucher issuer i is told by the VTP to Cancel. *) + (*************************************************************************) + /\ vState \in [V -> {"valid", "cancelled"}] + /\ vlcState \in [V -> {"working", "done"}] + /\ iState[i] = "waiting" + /\ [type |-> "Cancel"] \in msgs + /\ iState' = [iState EXCEPT ![i] = "cancelled"] + /\ UNCHANGED <> + +IRcvAbortMsg(i) == + (*************************************************************************) + (* Voucher issuer i is told by the VTP to abort. *) + (*************************************************************************) + /\ vState = [v \in V |-> "valid"] + /\ vlcState = [v \in V |-> "working"] + /\ iState[i] = "waiting" + /\ [type |-> "Abort"] \in msgs + /\ iState' = [iState EXCEPT ![i] = "aborted"] + /\ UNCHANGED <> + +VTPNext == + \/ \E v \in V: + VTPCancel(v) \/ VTPAbort(v) + \/ \E h,i \in H \cup I: + VTPRcvPrepared(h,i) + \/ \E h \in H: + HPrepare(h) \/ HChooseToAbort(h) + \/ HRcvAbortMsg(h) \/ HRcvCancelMsg(h) + \/ \E i \in I: + IPrepare(i) \/ IChooseToAbort(i) + \/ IRcvAbortMsg(i) \/ IRcvCancelMsg(i) +----------------------------------------------------------------------------- +VTPConsistent == + (*************************************************************************) + (* A state predicate asserting that a H and an I have not reached *) + (* conflicting decisions. It is an invariant of the specification. *) + (*************************************************************************) + /\ \A h \in H, i \in I : /\ ~ /\ hState[h] = "cancelled" + /\ iState[i] = "aborted" + /\ ~ /\ hState[h] = "aborted" + /\ iState[i] = "cancelled" +----------------------------------------------------------------------------- +VTPVars == <> + +VTPSpec == VTPInit /\ [][VTPNext]_VTPVars + (*************************************************************************) + (* The complete spec of the a Voucher Cancel using Two-Phase Commit *) + (* protocol. *) + (*************************************************************************) + +THEOREM VTPSpec => [](VTPTypeOK /\ VTPConsistent) + (*************************************************************************) + (* This theorem asserts the truth of the temporal formula whose meaning *) + (* is that the state predicate VTPTypeOK /\ VTPConsistent is an *) + (* invariant of the specification VTPSpec. Invariance of this *) + (* conjunction is equivalent to invariance of both of the formulas *) + (* VTPTypeOK and VTPConsistent. *) + (*************************************************************************) +----------------------------------------------------------------------------- +(***************************************************************************) +(* We now assert that the Voucher Cancel specification implements the *) +(* Voucher Life Cycle specification of a voucher mentioned in module *) +(* VoucherLifeCycle. The following statement imports all the definitions *) +(* from module VoucherLifeCycle into the current module. *) +(***************************************************************************) +INSTANCE VoucherLifeCycle + +THEOREM VTPSpec => VSpec + (*************************************************************************) + (* This theorem asserts that the specification VTPSpec of the Two-Phase *) + (* Commit protocol implements the specification VSpec of the *) + (* Voucher life cycle specification. *) + (*************************************************************************) +============================================================================= +\* Modification History +\* Last modified Tue Jun 12 13:03:21 IST 2018 by Fox +\* Created Fri Mar 16 17:45:37 SGT 2018 by Fox diff --git a/specifications/byihive/VoucherIssue.cfg b/specifications/byihive/VoucherIssue.cfg new file mode 100644 index 00000000..10183c3c --- /dev/null +++ b/specifications/byihive/VoucherIssue.cfg @@ -0,0 +1,9 @@ +CONSTANTS + V = {v1, v2, v3} + H = {holder1, holder2, holder3} + I = {issuer1, issuer2, issuer3} +INVARIANTS VTPTypeOK VTPConsistent +SPECIFICATION VTPSpec +PROPERTY VSpec +CHECK_DEADLOCK FALSE + diff --git a/specifications/byihive/VoucherIssue.tla b/specifications/byihive/VoucherIssue.tla new file mode 100644 index 00000000..ff05ec3e --- /dev/null +++ b/specifications/byihive/VoucherIssue.tla @@ -0,0 +1,287 @@ +\* Copyright (c) 2018, Backyard Innovations Pte. Ltd., Singapore. +\* +\* Released under the terms of the Apache License 2.0 +\* See: file LICENSE that came with this software for details. +\* +\* This file contains Intellectual Property that belongs to +\* Backyard Innovations Pte Ltd., Singapore. +\* +\* Authors: Santhosh Raju +\* Cherry G. Mathew +\* Fransisca Andriani +\* +---------------------------- MODULE VoucherIssue ---------------------------- +(***************************************************************************) +(* The description is based on the "Issue" operation mentioned in RFC *) +(* 3506. This specification describes the issue of Voucher between an *) +(* Issuer and a Holder. It is implemented over the Two-Phase Commit *) +(* protocol, in which a Voucher Transaction Provider (VTP) coordinates the *) +(* Voucher Issuers (Is) to issue vouchers (Vs) to Voucher Holders (Hs) as *) +(* described in the VoucherLifeCycle specification module. In this *) +(* specification, Hs and Is spontaneously issue Prepared messages. We *) +(* ignore the Prepare messages that the VTP can send to the Hs and Is. *) +(* *) +(* For simplicity, we also eliminate Abort messages sent by an Hs / Is *) +(* when it decides to abort. Such a message would cause the VTP to abort *) +(* the transaction, an event represented here by the VTP spontaneously *) +(* deciding to abort. *) +(* *) +(* Note: We use the "phantom" state of a voucher before issuing a voucher. *) +(* Once the voucher is issued it goes to "valid" state. *) +(***************************************************************************) +CONSTANT + V, \* The set of Vouchers + H, \* The set of Voucher Holders + I \* The set of Voucher Issuers + +VARIABLES + vState, \* vState[v] is the state of voucher v. + vlcState, \* vlcState[v] is the state of the voucher life cycle + \* machine. + hState, \* hState[h] is the state of voucher holder h. + iState, \* iState[i] is the state of voucher issuer i. + vtpState, \* The state of the voucher transaction provider. + vtpIPrepared, \* The set of Hs and Is from which the VTP has received + \* "Prepared for Voucher Issue" messages. + msgs + (***********************************************************************) + (* In the protocol, processes communicate with one another by sending *) + (* messages. For simplicity, we represent message passing with the *) + (* variable msgs whose value is the set of all messages that have been *) + (* sent. A message is sent by adding it to the set msgs. An action *) + (* that, in an implementation, would be enabled by the receipt of a *) + (* certain message is here enabled by the presence of that message in *) + (* msgs. For simplicity, messages are never removed from msgs. This *) + (* allows a single message to be received by multiple receivers. *) + (* Receipt of the same message twice is therefore allowed; but in this *) + (* particular protocol, that's not a problem. *) + (***********************************************************************) + +Messages == + (*************************************************************************) + (* The set of all possible messages. Messages of type "Prepared" are *) + (* sent from the H indicated by the message's vh field to the VTP. *) + (* Similar "Prepared" is also sent from I indicated by message's vc *) + (* field to the VTP. Messages of type "Issue" and "Abort" are broadcast *) + (* by the VTPs, to be received by all Hs and Is. The set msgs contains *) + (* just a single copy of such a message. *) + (*************************************************************************) + [type : {"Prepared"}, vi : I] \cup + [type : {"Prepared"}, vh : H] \cup + [type : {"Issue", "Abort"}] + +VTPTypeOK == + (*************************************************************************) + (* The type-correctness invariant *) + (*************************************************************************) + /\ vState \in [V -> {"phantom", "valid"}] + /\ vlcState \in [V -> {"init", "working"}] + /\ hState \in [H -> {"waiting", "prepared", "holding", "aborted"}] + /\ iState \in [I -> {"waiting", "prepared", "issued", "aborted"}] + /\ vtpState \in {"init", "done"} + /\ vtpIPrepared \subseteq (H \cup I) + /\ msgs \subseteq Messages + +VTPInit == + (*************************************************************************) + (* The initial predicate. *) + (*************************************************************************) + /\ vState = [v \in V |-> "phantom"] + /\ vlcState = [v \in V |-> "init"] + /\ hState = [h \in H |-> "waiting"] + /\ iState = [i \in I |-> "waiting"] + /\ vtpState = "init" + /\ vtpIPrepared = {} + /\ msgs = {} +----------------------------------------------------------------------------- +(***************************************************************************) +(* We now define the actions that may be performed by the processes, first *) +(* the VTP's actions, the Hs' actions, then the Is' actions. *) +(***************************************************************************) +VTPRcvPrepared(h,i) == + (*************************************************************************) + (* The VTP receives a "Prepared" message from Voucher Holder h and the *) + (* Voucher Issuer i. We could add the additional enabling condition *) + (* h,i \notin vtpIPrepared, which disables the action if the VTP has *) + (* already received this message. But there is no need, because in that *) + (* case the action has no effect; it leaves the state unchanged. *) + (*************************************************************************) + /\ vState = [v \in V |-> "phantom"] + /\ vlcState = [v \in V |-> "init"] + /\ vtpState = "init" + /\ [type |-> "Prepared", vh |-> h] \in msgs + /\ [type |-> "Prepared", vi |-> i] \in msgs + /\ vtpIPrepared' = vtpIPrepared \cup {h,i} + /\ UNCHANGED <> + +VTPIssue(v) == + (*************************************************************************) + (* The VTP Issues the voucher; enabled iff the VTP is in its *) + (* initial state and every H and I has sent a "Prepared" message. *) + (*************************************************************************) + /\ vState[v] = "phantom" + /\ vlcState[v] = "init" + /\ vtpState = "init" + /\ vtpIPrepared = H \cup I + /\ vtpState' = "done" + /\ vState' = [vState EXCEPT ![v] = "valid"] + /\ vlcState' = [vlcState EXCEPT ![v] = "working"] + /\ msgs' = msgs \cup {[type |-> "Issue"]} + /\ UNCHANGED <> + +VTPAbort(v) == + (*************************************************************************) + (* The VTP spontaneously aborts the transaction. *) + (*************************************************************************) + /\ vState[v] = "phantom" + /\ vlcState[v] = "init" + /\ vtpState = "init" + /\ vtpState' = "done" + /\ msgs' = msgs \cup {[type |-> "Abort"]} + /\ UNCHANGED <> + +HPrepare(h) == + (*************************************************************************) + (* Voucher holder h prepares. *) + (*************************************************************************) + /\ vState = [v \in V |-> "phantom"] + /\ vlcState = [v \in V |-> "init"] + /\ hState[h] = "waiting" + /\ hState' = [hState EXCEPT ![h] = "prepared"] + /\ msgs' = msgs \cup {[type |-> "Prepared", vh |-> h]} + /\ UNCHANGED <> + +HChooseToAbort(h) == + (*************************************************************************) + (* Voucher holder h spontaneously decides to abort. As noted above, h *) + (* does not send any message in our simplified spec. *) + (*************************************************************************) + /\ vState = [v \in V |-> "phantom"] + /\ vlcState = [v \in V |-> "init"] + /\ hState[h] = "waiting" + /\ hState' = [hState EXCEPT ![h] = "aborted"] + /\ UNCHANGED <> + +HRcvIssueMsg(h) == + (*************************************************************************) + (* Voucher holder h is told by the VTP to Issue. *) + (*************************************************************************) + /\ vState \in [V -> {"phantom", "valid"}] + /\ vlcState \in [V -> {"init", "working"}] + /\ hState[h] = "waiting" + /\ [type |-> "Issue"] \in msgs + /\ hState' = [hState EXCEPT ![h] = "holding"] + /\ UNCHANGED <> + +HRcvAbortMsg(h) == + (*************************************************************************) + (* Voucher holder h is told by the VTP to abort. *) + (*************************************************************************) + /\ vState = [v \in V |-> "phantom"] + /\ vlcState = [v \in V |-> "init"] + /\ hState[h] = "waiting" + /\ [type |-> "Abort"] \in msgs + /\ hState' = [hState EXCEPT ![h] = "aborted"] + /\ UNCHANGED <> + +IPrepare(i) == + (*************************************************************************) + (* Voucher issuer i prepares. *) + (*************************************************************************) + /\ vState = [v \in V |-> "phantom"] + /\ vlcState = [v \in V |-> "init"] + /\ iState[i] = "waiting" + /\ iState' = [iState EXCEPT ![i] = "prepared"] + /\ msgs' = msgs \cup {[type |-> "Prepared", vi |-> i]} + /\ UNCHANGED <> + +IChooseToAbort(i) == + (*************************************************************************) + (* Voucher issuer i spontaneously decides to abort. As noted above, i *) + (* does not send any message in our simplified spec. *) + (*************************************************************************) + /\ vState = [v \in V |-> "phantom"] + /\ vlcState = [v \in V |-> "init"] + /\ iState[i] = "waiting" + /\ iState' = [iState EXCEPT ![i] = "aborted"] + /\ UNCHANGED <> + +IRcvIssueMsg(i) == + (*************************************************************************) + (* Voucher issuer i is told by the VTP to Issue. *) + (*************************************************************************) + /\ vState \in [V -> {"phantom", "valid"}] + /\ vlcState \in [V -> {"init", "working"}] + /\ iState[i] = "waiting" + /\ [type |-> "Issue"] \in msgs + /\ iState' = [iState EXCEPT ![i] = "issued"] + /\ UNCHANGED <> + +IRcvAbortMsg(i) == + (*************************************************************************) + (* Voucher issuer i is told by the VTP to abort. *) + (*************************************************************************) + /\ vState = [v \in V |-> "phantom"] + /\ vlcState = [v \in V |-> "init"] + /\ iState[i] = "waiting" + /\ [type |-> "Abort"] \in msgs + /\ iState' = [iState EXCEPT ![i] = "aborted"] + /\ UNCHANGED <> + +VTPNext == + \/ \E v \in V: + VTPIssue(v) \/ VTPAbort(v) + \/ \E h,i \in H \cup I: + VTPRcvPrepared(h,i) + \/ \E h \in H: + HPrepare(h) \/ HChooseToAbort(h) + \/ HRcvAbortMsg(h) \/ HRcvIssueMsg(h) + \/ \E i \in I: + IPrepare(i) \/ IChooseToAbort(i) + \/ IRcvAbortMsg(i) \/ IRcvIssueMsg(i) +----------------------------------------------------------------------------- +VTPConsistent == + (*************************************************************************) + (* A state predicate asserting that a H and an I have not reached *) + (* conflicting decisions. It is an invariant of the specification. *) + (*************************************************************************) + /\ \A h \in H, i \in I : /\ ~ /\ hState[h] = "holding" + /\ iState[i] = "aborted" + /\ ~ /\ hState[h] = "aborted" + /\ iState[i] = "issued" +----------------------------------------------------------------------------- +VTPVars == <> + +VTPSpec == VTPInit /\ [][VTPNext]_VTPVars + (*************************************************************************) + (* The complete spec of the a Voucher Issue using Two-Phase Commit *) + (* protocol. *) + (*************************************************************************) + +THEOREM VTPSpec => [](VTPTypeOK /\ VTPConsistent) + (*************************************************************************) + (* This theorem asserts the truth of the temporal formula whose meaning *) + (* is that the state predicate VTPTypeOK /\ VTPConsistent is an *) + (* invariant of the specification VTPSpec. Invariance of this *) + (* conjunction is equivalent to invariance of both of the formulas *) + (* VTPTypeOK and VTPConsistent. *) + (*************************************************************************) +----------------------------------------------------------------------------- +(***************************************************************************) +(* We now assert that the Voucher Issue specification implements the *) +(* Voucher Life Cycle specification of a voucher mentioned in module *) +(* VoucherLifeCycle. The following statement imports all the definitions *) +(* from module VoucherLifeCycle into the current module. *) +(***************************************************************************) +INSTANCE VoucherLifeCycle + +THEOREM VTPSpec => VSpec + (*************************************************************************) + (* This theorem asserts that the specification VTPSpec of the Two-Phase *) + (* Commit protocol implements the specification VSpec of the *) + (* Voucher life cycle specification. *) + (*************************************************************************) +============================================================================= +\* Modification History +\* Last modified Tue Jun 12 13:33:03 IST 2018 by Fox +\* Created Fri Mar 16 17:45:37 SGT 2018 by Fox diff --git a/specifications/byihive/VoucherLifeCycle.cfg b/specifications/byihive/VoucherLifeCycle.cfg new file mode 100644 index 00000000..918527b3 --- /dev/null +++ b/specifications/byihive/VoucherLifeCycle.cfg @@ -0,0 +1,5 @@ +CONSTANT V = {v1, v2, v3} +INVARIANTS VTypeOK VConsistent +SPECIFICATION VSpec +CHECK_DEADLOCK FALSE + diff --git a/specifications/byihive/VoucherLifeCycle.tla b/specifications/byihive/VoucherLifeCycle.tla new file mode 100644 index 00000000..dab3db34 --- /dev/null +++ b/specifications/byihive/VoucherLifeCycle.tla @@ -0,0 +1,107 @@ +\* Copyright (c) 2018, Backyard Innovations Pte. Ltd., Singapore. +\* +\* Released under the terms of the Apache License 2.0 +\* See: file LICENSE that came with this software for details. +\* +\* This file contains Intellectual Property that belongs to +\* Backyard Innovations Pte Ltd., Singapore. +\* +\* Authors: Santhosh Raju +\* Cherry G. Mathew +\* Fransisca Andriani +\* +-------------------------- MODULE VoucherLifeCycle -------------------------- +(***************************************************************************) +(* This specification is of a Voucher and it's life cycle. This is based *) +(* on the definiton of Vouchers in RFC 3506 with the tuple part decoupled. *) +(* *) +(* Note: A new state called "phantom" was introduced to indicate the state *) +(* of a voucher that is yet to be issued, once a voucher is issued it *) +(* becomes a "valid" voucher. This is a one way transition and it cannot *) +(* reversed. *) +(***************************************************************************) +CONSTANT V \* The set of vouchers. + +VARIABLE vState, \* vState[v] is the state of a voucher v. + vlcState \* The state of the voucher life cycle machine. + \* vvlcState[v] is the state of the life cycle machine + \* for the voucher v. +----------------------------------------------------------------------------- +VTypeOK == + (*************************************************************************) + (* The type-correctness invariant *) + (*************************************************************************) + /\ vState \in [V -> {"phantom", "valid", "redeemed", "cancelled"}] + /\ vlcState \in [V -> {"init", "working", "done"}] + +VInit == + (*************************************************************************) + (* The initial predicate. *) + (*************************************************************************) + /\ vState = [v \in V |-> "phantom"] + /\ vlcState = [v \in V |-> "init"] +----------------------------------------------------------------------------- +(***************************************************************************) +(* We now define the actions that may be performed on the Vs, and then *) +(* define the complete next-state action of the specification to be the *) +(* disjunction of the possible V actions. *) +(***************************************************************************) +Issue(v) == + /\ vState[v] = "phantom" + /\ vlcState[v] = "init" + /\ vState' = [vState EXCEPT ![v] = "valid"] + /\ vlcState' = [vlcState EXCEPT ![v] = "working"] + +Transfer(v) == + /\ vState[v] = "valid" + /\ UNCHANGED <> + +Redeem(v) == + /\ vState[v] = "valid" + /\ vlcState[v] = "working" + /\ vState' = [vState EXCEPT ![v] = "redeemed"] + /\ vlcState' = [vlcState EXCEPT ![v] = "done"] + +Cancel(v) == + /\ vState[v] = "valid" + /\ vlcState[v] = "working" + /\ vState' = [vState EXCEPT ![v] = "cancelled"] + /\ vlcState' = [vlcState EXCEPT ![v] = "done"] + +VNext == \E v \in V : Issue(v) \/ Redeem(v) \/ Transfer(v) \/ Cancel(v) + (*************************************************************************) + (* The next-state action. *) + (*************************************************************************) +----------------------------------------------------------------------------- +VConsistent == + (*************************************************************************) + (* A state predicate asserting that a V started at a valid start state *) + (* and has reached a valid final state at the end of the life cycle. *) + (* V can be "valid" only when the state of the machine is "working". *) + (* It is an invariant of the specification. *) + (*************************************************************************) + /\ \A v \in V : \/ /\ vlcState[v] = "done" + /\ vState[v] \in {"redeemed", "cancelled"} + \/ /\ vlcState[v] = "init" + /\ vState[v] = "phantom" + \/ /\ vlcState[v] = "working" + /\ vState[v] \in {"valid"} +----------------------------------------------------------------------------- +VSpec == VInit /\ [][VNext]_<> + (*************************************************************************) + (* The complete specification of the protocol written as a temporal *) + (* formula. *) + (*************************************************************************) + +THEOREM VSpec => [](VTypeOK /\ VConsistent) + (*************************************************************************) + (* This theorem asserts the truth of the temporal formula whose meaning *) + (* is that the state predicate VTypeOK /\ VConsistent is an invariant *) + (* of the specification VSpec. Invariance of this conjunction is *) + (* equivalent to invariance of both of the formulas VTypeOK and *) + (* VConsistent. *) + (*************************************************************************) +============================================================================= +\* Modification History +\* Last modified Tue Jun 12 13:25:29 IST 2018 by Fox +\* Created Fri Mar 16 11:56:25 SGT 2018 by Fox diff --git a/specifications/byihive/VoucherRedeem.cfg b/specifications/byihive/VoucherRedeem.cfg new file mode 100644 index 00000000..48319f06 --- /dev/null +++ b/specifications/byihive/VoucherRedeem.cfg @@ -0,0 +1,10 @@ +CONSTANTS + V = {v1, v2, v3} + H = {holder1, holder2, holder3} + C = {collector1, collector2, collector3} +INVARIANTS VTPTypeOK VTPConsistent +SPECIFICATION VTPSpec +\* Refinement does not hold +\*PROPERTY VSpec +CHECK_DEADLOCK FALSE + diff --git a/specifications/byihive/VoucherRedeem.tla b/specifications/byihive/VoucherRedeem.tla new file mode 100644 index 00000000..cbc1c63b --- /dev/null +++ b/specifications/byihive/VoucherRedeem.tla @@ -0,0 +1,284 @@ +\* Copyright (c) 2018, Backyard Innovations Pte. Ltd., Singapore. +\* +\* Released under the terms of the Apache License 2.0 +\* See: file LICENSE that came with this software for details. +\* +\* This file contains Intellectual Property that belongs to +\* Backyard Innovations Pte Ltd., Singapore. +\* +\* Authors: Santhosh Raju +\* Cherry G. Mathew +\* Fransisca Andriani +\* +--------------------------- MODULE VoucherRedeem ---------------------------- +(***************************************************************************) +(* The description is based on the "Redeem" operation mentioned in RFC *) +(* 3506. This specification describes the redemption of Voucher between *) +(* a Holder and Collector. It is implemented over the Two-Phase Commit *) +(* protocol, in which a Voucher Transaction Provider (VTP) coordinates the *) +(* Voucher Holders (Hs) to redeem vouchers (Vs) to Voucher Collectors (Cs) *) +(* described in the VoucherLifeCycle specification module. In this *) +(* specification, Hs and Cs spontaneously issue Prepared messages. We *) +(* ignore the Prepare messages that the VTP can send to the Hs and Cs. *) +(* *) +(* For simplicity, we also eliminate Abort messages sent by an Hs / Cs *) +(* when it decides to abort. Such a message would cause the VTP to abort *) +(* the transaction, an event represented here by the VTP spontaneously *) +(* deciding to abort. *) +(***************************************************************************) +CONSTANT + V, \* The set of Vouchers + H, \* The set of Voucher Holders + C \* The set of Voucher Collectors + +VARIABLES + vState, \* vState[v] is the state of voucher v. + vlcState, \* vlcState[v] is the state of the voucher life cycle + \* machine. + hState, \* hState[h] is the state of voucher holder h. + cState, \* cState[c] is the state of voucher collector c. + vtpState, \* The state of the voucher transaction provider. + vtpRPrepared, \* The set of Hs and Cs from which the VTP has received + \* "Prepared for Voucher Redeem" messages. + msgs + (***********************************************************************) + (* In the protocol, processes communicate with one another by sending *) + (* messages. For simplicity, we represent message passing with the *) + (* variable msgs whose value is the set of all messages that have been *) + (* sent. A message is sent by adding it to the set msgs. An action *) + (* that, in an implementation, would be enabled by the receipt of a *) + (* certain message is here enabled by the presence of that message in *) + (* msgs. For simplicity, messages are never removed from msgs. This *) + (* allows a single message to be received by multiple receivers. *) + (* Receipt of the same message twice is therefore allowed; but in this *) + (* particular protocol, that's not a problem. *) + (***********************************************************************) + +Messages == + (*************************************************************************) + (* The set of all possible messages. Messages of type "Prepared" are *) + (* sent from the H indicated by the message's vh field to the VTP. *) + (* Similar "Prepared" is also sent from C indicated by message's vc *) + (* field to the VTP. Messages of type "Redeem" and "Abort" are broadcast *) + (* by the VTPs, to be received by all Hs and Cs. The set msgs contains *) + (* just a single copy of such a message. *) + (*************************************************************************) + [type : {"Prepared"}, vh : H] \cup + [type : {"Prepared"}, vc : C] \cup + [type : {"Redeem", "Abort"}] + +VTPTypeOK == + (*************************************************************************) + (* The type-correctness invariant *) + (*************************************************************************) + /\ vState \in [V -> {"valid", "redeemed"}] + /\ vlcState \in [V -> {"working", "done"}] + /\ hState \in [H -> {"holding", "prepared", "redeemed", "aborted"}] + /\ cState \in [C -> {"waiting", "prepared", "redeemed", "aborted"}] + /\ vtpState \in {"init", "done"} + /\ vtpRPrepared \subseteq (H \cup C) + /\ msgs \subseteq Messages + +VTPInit == + (*************************************************************************) + (* The initial predicate. *) + (*************************************************************************) + /\ vState = [v \in V |-> "valid"] + /\ vlcState = [v \in V |-> "working"] + /\ hState = [h \in H |-> "holding"] + /\ cState = [c \in C |-> "waiting"] + /\ vtpState = "init" + /\ vtpRPrepared = {} + /\ msgs = {} +----------------------------------------------------------------------------- +(***************************************************************************) +(* We now define the actions that may be performed by the processes, first *) +(* the VTP's actions, the Hs' actions, then the Cs' actions. *) +(***************************************************************************) +VTPRcvPrepared(h,c) == + (*************************************************************************) + (* The VTP receives a "Prepared" message from Voucher Holder h and the *) + (* Voucher Collector c. We could add the additional enabling condition *) + (* h,c \notin vtpRPrepared, which disables the action if the VTP has *) + (* already received this message. But there is no need, because in that *) + (* case the action has no effect; it leaves the state unchanged. *) + (*************************************************************************) + /\ vState = [v \in V |-> "valid"] + /\ vlcState = [v \in V |-> "working"] + /\ vtpState = "init" + /\ [type |-> "Prepared", vh |-> h] \in msgs + /\ [type |-> "Prepared", vc |-> c] \in msgs + /\ vtpRPrepared' = vtpRPrepared \cup {h,c} + /\ UNCHANGED <> + +VTPRedeem(v) == + (*************************************************************************) + (* The VTP Redeems the voucher; enabled iff the VTP is in its *) + (* initial state and every H and C has sent a "Prepared" message. *) + (*************************************************************************) + /\ vState[v] = "valid" + /\ vlcState[v] = "working" + /\ vtpState = "init" + /\ vtpRPrepared = H \cup C + /\ vtpState' = "done" + /\ vState' = [vState EXCEPT ![v] = "redeemed"] + /\ vlcState' = [vlcState EXCEPT ![v] = "done"] + /\ msgs' = msgs \cup {[type |-> "Redeem"]} + /\ UNCHANGED <> + +VTPAbort(v) == + (*************************************************************************) + (* The VTP spontaneously aborts the transaction. *) + (*************************************************************************) + /\ vState[v] = "valid" + /\ vlcState[v] = "working" + /\ vtpState = "init" + /\ vtpState' = "done" + /\ msgs' = msgs \cup {[type |-> "Abort"]} + /\ UNCHANGED <> + +HPrepare(h) == + (*************************************************************************) + (* Voucher holder h prepares. *) + (*************************************************************************) + /\ vState = [v \in V |-> "valid"] + /\ vlcState = [v \in V |-> "working"] + /\ hState[h] = "holding" + /\ hState' = [hState EXCEPT ![h] = "prepared"] + /\ msgs' = msgs \cup {[type |-> "Prepared", vh |-> h]} + /\ UNCHANGED <> + +HChooseToAbort(h) == + (*************************************************************************) + (* Voucher holder h spontaneously decides to abort. As noted above, h *) + (* does not send any message in our simplified spec. *) + (*************************************************************************) + /\ vState = [v \in V |-> "valid"] + /\ vlcState = [v \in V |-> "working"] + /\ hState[h] = "holding" + /\ hState' = [hState EXCEPT ![h] = "aborted"] + /\ UNCHANGED <> + +HRcvRedeemMsg(h) == + (*************************************************************************) + (* Voucher holder h is told by the VTP to Redeem. *) + (*************************************************************************) + /\ vState \in [V -> {"valid", "redeemed"}] + /\ vlcState \in [V -> {"working", "done"}] + /\ hState[h] = "holding" + /\ [type |-> "Redeem"] \in msgs + /\ hState' = [hState EXCEPT ![h] = "redeemed"] + /\ UNCHANGED <> + +HRcvAbortMsg(h) == + (*************************************************************************) + (* Voucher holder h is told by the VTP to abort. *) + (*************************************************************************) + /\ vState = [v \in V |-> "valid"] + /\ vlcState = [v \in V |-> "working"] + /\ hState[h] = "holding" + /\ [type |-> "Abort"] \in msgs + /\ hState' = [hState EXCEPT ![h] = "aborted"] + /\ UNCHANGED <> + +CPrepare(c) == + (*************************************************************************) + (* Voucher collector c prepares. *) + (*************************************************************************) + /\ vState = [v \in V |-> "valid"] + /\ vlcState = [v \in V |-> "working"] + /\ cState[c] = "waiting" + /\ cState' = [cState EXCEPT ![c] = "prepared"] + /\ msgs' = msgs \cup {[type |-> "Prepared", vc |-> c]} + /\ UNCHANGED <> + +CChooseToAbort(c) == + (*************************************************************************) + (* Voucher collector c spontaneously decides to abort. As noted above, c *) + (* does not send any message in our simplified spec. *) + (*************************************************************************) + /\ vState = [v \in V |-> "valid"] + /\ vlcState = [v \in V |-> "working"] + /\ cState[c] = "waiting" + /\ cState' = [cState EXCEPT ![c] = "aborted"] + /\ UNCHANGED <> + +CRcvRedeemMsg(c) == + (*************************************************************************) + (* Voucher collector c is told by the VTP to Redeem. *) + (*************************************************************************) + /\ vState \in [V -> {"valid", "redeemed"}] + /\ vlcState \in [V -> {"working", "done"}] + /\ cState[c] = "waiting" + /\ [type |-> "Redeem"] \in msgs + /\ cState' = [cState EXCEPT ![c] = "redeemed"] + /\ UNCHANGED <> + +CRcvAbortMsg(c) == + (*************************************************************************) + (* Voucher collector c is told by the VTP to abort. *) + (*************************************************************************) + /\ vState = [v \in V |-> "valid"] + /\ vlcState = [v \in V |-> "working"] + /\ cState[c] = "waiting" + /\ [type |-> "Abort"] \in msgs + /\ cState' = [cState EXCEPT ![c] = "aborted"] + /\ UNCHANGED <> + +VTPNext == + \/ \E v \in V: + VTPRedeem(v) \/ VTPAbort(v) + \/ \E h,c \in H \cup C: + VTPRcvPrepared(h,c) + \/ \E h \in H: + HPrepare(h) \/ HChooseToAbort(h) + \/ HRcvAbortMsg(h) \/ HRcvRedeemMsg(h) + \/ \E c \in C: + CPrepare(c) \/ CChooseToAbort(c) + \/ CRcvAbortMsg(c) \/ CRcvRedeemMsg(c) +----------------------------------------------------------------------------- +VTPConsistent == + (*************************************************************************) + (* A state predicate asserting that a H and an C have not reached *) + (* conflicting decisions. It is an invariant of the specification. *) + (*************************************************************************) + /\ \A h \in H, c \in C : /\ ~ /\ hState[h] = "redeemed" + /\ cState[c] = "aborted" + /\ ~ /\ hState[h] = "aborted" + /\ cState[c] = "redeemed" +----------------------------------------------------------------------------- +VTPVars == <> + +VTPSpec == VTPInit /\ [][VTPNext]_VTPVars + (*************************************************************************) + (* The complete spec of the a Voucher Redeem using Two-Phase Commit *) + (* protocol. *) + (*************************************************************************) + +THEOREM VTPSpec => [](VTPTypeOK /\ VTPConsistent) + (*************************************************************************) + (* This theorem asserts the truth of the temporal formula whose meaning *) + (* is that the state predicate VTPTypeOK /\ VTPConsistent is an *) + (* invariant of the specification VTPSpec. Invariance of this *) + (* conjunction is equivalent to invariance of both of the formulas *) + (* VTPTypeOK and VTPConsistent. *) + (*************************************************************************) +----------------------------------------------------------------------------- +(***************************************************************************) +(* We now assert that the Voucher Redeem specification implements the *) +(* Voucher Life Cycle specification of a voucher mentioned in module *) +(* VoucherLifeCycle. The following statement imports all the definitions *) +(* from module VoucherLifeCycle into the current module. *) +(***************************************************************************) +INSTANCE VoucherLifeCycle + +THEOREM VTPSpec => VSpec + (*************************************************************************) + (* This theorem asserts that the specification VTPSpec of the Two-Phase *) + (* Commit protocol implements the specification VSpec of the *) + (* Voucher life cycle specification. *) + (*************************************************************************) +============================================================================= +\* Modification History +\* Last modified Tue Jun 12 13:35:49 IST 2018 by Fox +\* Created Fri Mar 16 17:45:37 SGT 2018 by Fox diff --git a/specifications/byihive/VoucherTransfer.cfg b/specifications/byihive/VoucherTransfer.cfg new file mode 100644 index 00000000..fefd1c0d --- /dev/null +++ b/specifications/byihive/VoucherTransfer.cfg @@ -0,0 +1,10 @@ +CONSTANTS + V = {v1, v2, v3} + SH = {src1, src2, src3} + DH = {dst1, dst2, dst3} +INVARIANTS VTPTypeOK VTPConsistent +SPECIFICATION VTPSpec +\* Refinement does not hold +\*PROPERTY VSpec +CHECK_DEADLOCK FALSE + diff --git a/specifications/byihive/VoucherTransfer.tla b/specifications/byihive/VoucherTransfer.tla new file mode 100644 index 00000000..691efda5 --- /dev/null +++ b/specifications/byihive/VoucherTransfer.tla @@ -0,0 +1,293 @@ +\* Copyright (c) 2018, Backyard Innovations Pte. Ltd., Singapore. +\* +\* Released under the terms of the Apache License 2.0 +\* See: file LICENSE that came with this software for details. +\* +\* This file contains Intellectual Property that belongs to +\* Backyard Innovations Pte Ltd., Singapore. +\* +\* Authors: Santhosh Raju +\* Cherry G. Mathew +\* Fransisca Andriani +\* +-------------------------- MODULE VoucherTransfer -------------------------- + +(***************************************************************************) +(* The description is based on the "Transfer" operation mentioned in RFC *) +(* 3506. This specification describes the transfer of Voucher between two *) +(* Holders. It is implemented over the Two-Phase Commit protocol, in which *) +(* a Voucher Transaction Provider (VTP) coordinates the "Source" Voucher *) +(* Holders (SHs) to trade vouchers (Vs) to "Destination" Voucher Holders *) +(* (DHs) described in the VoucherLifeCycle specification module. In this *) +(* specification, SHs and DHs spontaneously issue Prepared messages. We *) +(* ignore the Prepare messages that the VTP can send to the SHs and DHs. *) +(* *) +(* For simplicity, we also eliminate Abort messages sent by an SHs and DHs *) +(* when it decides to abort. Such a message would cause the VTP to abort *) +(* the transaction, an event represented here by the VTP spontaneously *) +(* deciding to abort. *) +(* *) +(* Note: The RFC does not differentiate between a Holder who is initiating *) +(* the transfer (i.e. the holder of the voucher) and the Holder who is *) +(* receiving the voucher (i.e. the holder who would be the future owner of *) +(* this voucher). In order to make this distinction we have the "Source" *) +(* Voucher Holders (SHs), a subset of Holders who would like to transfer *) +(* an existing voucher they are "holding". We also have the "Destination" *) +(* Voucher Holders (DHs), a subset of Holders who are "waiting" to receive *) +(* the transferred vouchers. *) +(***************************************************************************) +CONSTANT + V, \* The set of Vouchers + SH, \* The set of "Source" Voucher Holders + DH \* The set of "Destination" Voucher Holders + +VARIABLES + vState, \* vState[v] is the state of voucher v. + vlcState, \* vlcState[v] is the state of the voucher life cycle + \* machine. + shState, \* shState[sh] is the state of "source" voucher holder sh. + dhState, \* dhState[dh] is the state of "destination" voucher holder dh. + vtpState, \* The state of the voucher transaction provider. + vtpTPrepared, \* The set of SHs and DHs from which the VTP has received + \* "Prepared for Voucher Transfer" messages. + msgs + (***********************************************************************) + (* In the protocol, processes communicate with one another by sending *) + (* messages. For simplicity, we represent message passing with the *) + (* variable msgs whose value is the set of all messages that have been *) + (* sent. A message is sent by adding it to the set msgs. An action *) + (* that, in an implementation, would be enabled by the receipt of a *) + (* certain message is here enabled by the presence of that message in *) + (* msgs. For simplicity, messages are never removed from msgs. This *) + (* allows a single message to be received by multiple receivers. *) + (* Receipt of the same message twice is therefore allowed; but in this *) + (* particular protocol, that's not a problem. *) + (***********************************************************************) + +Messages == + (*************************************************************************) + (* The set of all possible messages. Messages of type "Prepared" are *) + (* sent from the SH indicated by the message's vsh field to the VTP. *) + (* Similar "Prepared" is also sent from DH indicated by message's vdh *) + (* field to the VTP. Messages of type "Transfer" and "Abort" are *) + (* broadcast by the VTPs, to be received by all SHs and DHs. The set *) + (* msgs contains just a single copy of such a message. *) + (*************************************************************************) + [type : {"Prepared"}, vsh : SH] \cup + [type : {"Prepared"}, vdh : DH] \cup + [type : {"Transfer", "Abort"}] + +VTPTypeOK == + (*************************************************************************) + (* The type-correctness invariant *) + (*************************************************************************) + /\ vState \in [V -> {"valid"}] + /\ vlcState \in [V -> {"working"}] + /\ shState \in [SH -> {"holding", "prepared", "transferred", "aborted"}] + /\ dhState \in [DH -> {"waiting", "prepared", "holding", "aborted"}] + /\ vtpState \in {"init", "done"} + /\ vtpTPrepared \subseteq (SH \cup DH) + /\ msgs \subseteq Messages + +VTPInit == + (*************************************************************************) + (* The initial predicate. *) + (*************************************************************************) + /\ vState = [v \in V |-> "valid"] + /\ vlcState = [v \in V |-> "working"] + /\ shState = [sh \in SH |-> "holding"] + /\ dhState = [dh \in DH |-> "waiting"] + /\ vtpState = "init" + /\ vtpTPrepared = {} + /\ msgs = {} +----------------------------------------------------------------------------- +(***************************************************************************) +(* We now define the actions that may be performed by the processes, first *) +(* the VTP's actions, the SHs' actions, then the DHs' actions. *) +(***************************************************************************) +VTPRcvPrepared(sh,dh) == + (*************************************************************************) + (* The VTP receives a "Prepared" message from Source Voucher Holder sh *) + (* and the Destination Voucher Holder dh. We could add the additional *) + (* enabling condition sh,dh \not in vtpTPrepared, which disables the *) + (* action if the VTP has already received this message. But there is *) + (* no need, because in that case the action has no effect; it leaves the *) + (* state unchanged. *) + (*************************************************************************) + /\ vState = [v \in V |-> "valid"] + /\ vlcState = [v \in V |-> "working"] + /\ vtpState = "init" + /\ [type |-> "Prepared", vsh |-> sh] \in msgs + /\ [type |-> "Prepared", vdh |-> dh] \in msgs + /\ vtpTPrepared' = vtpTPrepared \cup {sh,dh} + /\ UNCHANGED <> + +VTPTransfer(v) == + (*************************************************************************) + (* The VTP Transfers the voucher; enabled iff the VTP is in its *) + (* initial state and every SH and DH has sent a "Prepared" message. *) + (*************************************************************************) + /\ vState[v] = "valid" + /\ vlcState[v] = "working" + /\ vtpState = "init" + /\ vtpTPrepared = SH \cup DH + /\ vtpState' = "done" + /\ msgs' = msgs \cup {[type |-> "Transfer"]} + /\ UNCHANGED <> + +VTPAbort(v) == + (*************************************************************************) + (* The VTP spontaneously aborts the transaction. *) + (*************************************************************************) + /\ vState[v] = "valid" + /\ vlcState[v] = "working" + /\ vtpState = "init" + /\ vtpState' = "done" + /\ msgs' = msgs \cup {[type |-> "Abort"]} + /\ UNCHANGED <> + +SHPrepare(sh) == + (*************************************************************************) + (* Source Voucher holder sh prepares. *) + (*************************************************************************) + /\ vState = [v \in V |-> "valid"] + /\ vlcState = [v \in V |-> "working"] + /\ shState[sh] = "holding" + /\ shState' = [shState EXCEPT ![sh] = "prepared"] + /\ msgs' = msgs \cup {[type |-> "Prepared", vsh |-> sh]} + /\ UNCHANGED <> + +SHChooseToAbort(sh) == + (*************************************************************************) + (* Source Voucher holder sh spontaneously decides to abort. As noted *) + (* above, sh does not send any message in our simplified spec. *) + (*************************************************************************) + /\ vState = [v \in V |-> "valid"] + /\ vlcState = [v \in V |-> "working"] + /\ shState[sh] = "holding" + /\ shState' = [shState EXCEPT ![sh] = "aborted"] + /\ UNCHANGED <> + +SHRcvTransferMsg(sh) == + (*************************************************************************) + (* Source Voucher holder sh is told by the VTP to Transfer. *) + (*************************************************************************) + /\ vState = [v \in V |-> "valid"] + /\ vlcState = [v \in V |-> "working"] + /\ shState[sh] = "holding" + /\ [type |-> "Transfer"] \in msgs + /\ shState' = [shState EXCEPT ![sh] = "transferred"] + /\ UNCHANGED <> + +SHRcvAbortMsg(sh) == + (*************************************************************************) + (* Source Voucher holder sh is told by the VTP to abort. *) + (*************************************************************************) + /\ vState = [v \in V |-> "valid"] + /\ vlcState = [v \in V |-> "working"] + /\ shState[sh] = "holding" + /\ [type |-> "Abort"] \in msgs + /\ shState' = [shState EXCEPT ![sh] = "aborted"] + /\ UNCHANGED <> + +DHPrepare(dh) == + (*************************************************************************) + (* Destination Voucher holder dh prepares. *) + (*************************************************************************) + /\ vState = [v \in V |-> "valid"] + /\ vlcState = [v \in V |-> "working"] + /\ dhState[dh] = "waiting" + /\ dhState' = [dhState EXCEPT ![dh] = "prepared"] + /\ msgs' = msgs \cup {[type |-> "Prepared", vdh |-> dh]} + /\ UNCHANGED <> + +DHChooseToAbort(dh) == + (*************************************************************************) + (* Destination Voucher holder dh spontaneously decides to abort. As *) + (* noted above, dh does not send any message in our simplified spec. *) + (*************************************************************************) + /\ vState = [v \in V |-> "valid"] + /\ vlcState = [v \in V |-> "working"] + /\ dhState[dh] = "waiting" + /\ dhState' = [dhState EXCEPT ![dh] = "aborted"] + /\ UNCHANGED <> + +DHRcvTransferMsg(dh) == + (*************************************************************************) + (* Destination Voucher holder dh is told by the VTP to Transfer. *) + (*************************************************************************) + /\ vState = [v \in V |-> "valid"] + /\ vlcState = [v \in V |-> "working"] + /\ dhState[dh] = "waiting" + /\ [type |-> "Transfer"] \in msgs + /\ dhState' = [dhState EXCEPT ![dh] = "holding"] + /\ UNCHANGED <> + +DHRcvAbortMsg(dh) == + (*************************************************************************) + (* Destination Voucher holder dh is told by the VTP to abort. *) + (*************************************************************************) + /\ vState = [v \in V |-> "valid"] + /\ vlcState = [v \in V |-> "working"] + /\ dhState[dh] = "waiting" + /\ [type |-> "Abort"] \in msgs + /\ dhState' = [dhState EXCEPT ![dh] = "aborted"] + /\ UNCHANGED <> + +VTPNext == + \/ \E v \in V: + VTPTransfer(v) \/ VTPAbort(v) + \/ \E sh,dh \in SH \cup DH: + VTPRcvPrepared(sh,dh) + \/ \E sh \in SH: + SHPrepare(sh) \/ SHChooseToAbort(sh) + \/ SHRcvAbortMsg(sh) \/ SHRcvTransferMsg(sh) + \/ \E dh \in DH: + DHPrepare(dh) \/ DHChooseToAbort(dh) + \/ DHRcvAbortMsg(dh) \/ DHRcvTransferMsg(dh) +----------------------------------------------------------------------------- +VTPConsistent == + (*************************************************************************) + (* A state predicate asserting that a SH and an DH have not reached *) + (* conflicting decisions. It is an invariant of the specification. *) + (*************************************************************************) + /\ \A sh \in SH, dh \in DH : /\ ~ /\ shState[sh] = "transferred" + /\ dhState[dh] = "aborted" + /\ ~ /\ shState[sh] = "aborted" + /\ dhState[dh] = "holding" +----------------------------------------------------------------------------- +VTPVars == <> + +VTPSpec == VTPInit /\ [][VTPNext]_VTPVars + (*************************************************************************) + (* The complete spec of the a Voucher Transfer using Two-Phase Commit *) + (* protocol. *) + (*************************************************************************) + +THEOREM VTPSpec => [](VTPTypeOK /\ VTPConsistent) + (*************************************************************************) + (* This theorem asserts the truth of the temporal formula whose meaning *) + (* is that the state predicate VTPTypeOK /\ VTPConsistent is an *) + (* invariant of the specification VTPSpec. Invariance of this *) + (* conjunction is equivalent to invariance of both of the formulas *) + (* VTPTypeOK and VTPConsistent. *) + (*************************************************************************) +----------------------------------------------------------------------------- +(***************************************************************************) +(* We now assert that the Voucher Transfer specification implements the *) +(* Voucher Life Cycle specification of a voucher mentioned in module *) +(* VoucherLifeCycle. The following statement imports all the definitions *) +(* from module VoucherLifeCycle into the current module. *) +(***************************************************************************) +INSTANCE VoucherLifeCycle + +THEOREM VTPSpec => VSpec + (*************************************************************************) + (* This theorem asserts that the specification VTPSpec of the Two-Phase *) + (* Commit protocol implements the specification VSpec of the *) + (* Voucher life cycle specification. *) + (*************************************************************************) +============================================================================= +\* Modification History +\* Last modified Tue Jun 12 13:15:55 IST 2018 by Fox +\* Created Fri Mar 16 17:45:37 SGT 2018 by Fox diff --git a/specifications/byzpaxos/BPConProof.cfg b/specifications/byzpaxos/BPConProof.cfg new file mode 100644 index 00000000..6de296db --- /dev/null +++ b/specifications/byzpaxos/BPConProof.cfg @@ -0,0 +1,11 @@ +CONSTANTS + Value = {v1, v2} + Acceptor = {a1, a2, a3} + FakeAcceptor = {fa1} + ByzQuorum = {{a1, a2, fa1}, {a1, a3, fa1}, {a2, a3, fa1}, {a1, a2, a3}} + WeakQuorum = {{a1, fa1}, {a2, fa1}, {a3, fa1}, {a1, a2}, {a1, a3}, {a2, a3}} + Ballot = {0, 1, 2} + None = None +INVARIANT Inv +SPECIFICATION Spec + diff --git a/specifications/byzpaxos/BPConProof.tla b/specifications/byzpaxos/BPConProof.tla new file mode 100644 index 00000000..17b6ae8a --- /dev/null +++ b/specifications/byzpaxos/BPConProof.tla @@ -0,0 +1,2127 @@ +---------------------------- MODULE BPConProof ------------------------------ +(***************************************************************************) +(* This module specifies a Byzantine Paxos algorithm--a version of Paxos *) +(* in which failed acceptors and leaders can be malicious. It is an *) +(* abstraction and generalization of the Castro-Liskov algorithm in *) +(* *) +(* author = "Miguel Castro and Barbara Liskov", *) +(* title = "Practical byzantine fault tolerance and proactive *) +(* recovery", *) +(* journal = ACM Transactions on Computer Systems, *) +(* volume = 20, *) +(* number = 4, *) +(* year = 2002, *) +(* pages = "398--461" *) +(***************************************************************************) + +EXTENDS Integers, FiniteSets, FiniteSetTheorems, TLAPS + +---------------------------------------------------------------------------- +(***************************************************************************) +(* The sets Value and Ballot are the same as in the Voting and *) +(* PConProof specs. *) +(***************************************************************************) +CONSTANT Value + +Ballot == Nat + +(***************************************************************************) +(* As in module PConProof, we define None to be an unspecified value that *) +(* is not an element of Value. *) +(***************************************************************************) +None == CHOOSE v : v \notin Value +----------------------------------------------------------------------------- +(***************************************************************************) +(* We pretend that which acceptors are good and which are malicious is *) +(* specified in advance. Of course, the algorithm executed by the good *) +(* acceptors makes no use of which acceptors are which. Hence, we can *) +(* think of the sets of good and malicious acceptors as "prophecy *) +(* constants" that are used only for showing that the algorithm implements *) +(* the PCon algorithm. *) +(* *) +(* We can assume that a maximal set of acceptors are bad, since a bad *) +(* acceptor is allowed to do anything--including acting like a good one. *) +(* *) +(* The basic idea is that the good acceptors try to execute the Paxos *) +(* consensus algorithm, while the bad acceptors may try to prevent them. *) +(* *) +(* We do not distinguish between faulty and non-faulty leaders. Safety *) +(* must be preserved even if all leaders are malicious, so we allow any *) +(* leader to send any syntactically correct message at any time. (In an *) +(* implementation, syntactically incorrect messages are simply ignored by *) +(* non-faulty acceptors and have no effect.) Assumptions about leader *) +(* behavior are required only for liveness. *) +(***************************************************************************) +CONSTANTS Acceptor, \* The set of good (non-faulty) acceptors. + FakeAcceptor, \* The set of possibly malicious (faulty) acceptors. + ByzQuorum, + (***************************************************************) + (* A Byzantine quorum is set of acceptors that includes a *) + (* quorum of good ones. In the case that there are 2f+1 good *) + (* acceptors and f bad ones, a Byzantine quorum is any set of *) + (* 2f+1 acceptors. *) + (***************************************************************) + WeakQuorum + (***************************************************************) + (* A weak quorum is a set of acceptors that includes at least *) + (* one good one. If there are f bad acceptors, then a weak *) + (* quorum is any set of f+1 acceptors. *) + (***************************************************************) + +(***************************************************************************) +(* We define ByzAcceptor to be the set of all real or fake acceptors. *) +(***************************************************************************) +ByzAcceptor == Acceptor \cup FakeAcceptor + +(***************************************************************************) +(* As in the Paxos consensus algorithm, we assume that the set of ballot *) +(* numbers and -1 is disjoint from the set of all (real and fake) *) +(* acceptors. *) +(***************************************************************************) +ASSUME BallotAssump == (Ballot \cup {-1}) \cap ByzAcceptor = {} + +(***************************************************************************) +(* The following are the assumptions about acceptors and quorums that are *) +(* needed to ensure safety of our algorithm. *) +(***************************************************************************) +ASSUME BQA == + /\ Acceptor \cap FakeAcceptor = {} + /\ \A Q \in ByzQuorum : Q \subseteq ByzAcceptor + /\ \A Q1, Q2 \in ByzQuorum : Q1 \cap Q2 \cap Acceptor # {} + /\ \A Q \in WeakQuorum : /\ Q \subseteq ByzAcceptor + /\ Q \cap Acceptor # {} + +(***************************************************************************) +(* The following assumption is not needed for safety, but it will be *) +(* needed to ensure liveness. *) +(***************************************************************************) +ASSUME BQLA == + /\ \E Q \in ByzQuorum : Q \subseteq Acceptor + /\ \E Q \in WeakQuorum : Q \subseteq Acceptor +----------------------------------------------------------------------------- +(***************************************************************************) +(* We now define the set BMessage of all possible messages. *) +(***************************************************************************) +1aMessage == [type : {"1a"}, bal : Ballot] + (*************************************************************************) + (* Type 1a messages are the same as in module PConProof. *) + (*************************************************************************) + +1bMessage == + (*************************************************************************) + (* A 1b message serves the same function as a 1b message in ordinary *) + (* Paxos, where the mbal and mval components correspond to the mbal and *) + (* mval components in the 1b messages of PConProof. The m2av component *) + (* is set containing all records with val and bal components equal to *) + (* the corresponding of components of a 2av message that the acceptor *) + (* has sent, except containing for each val only the record *) + (* corresponding to the 2av message with the highest bal component. *) + (*************************************************************************) + [type : {"1b"}, bal : Ballot, + mbal : Ballot \cup {-1}, mval : Value \cup {None}, + m2av : SUBSET [val : Value, bal : Ballot], + acc : ByzAcceptor] + +1cMessage == + (*************************************************************************) + (* Type 1c messages are the same as in PConProof. *) + (*************************************************************************) + [type : {"1c"}, bal : Ballot, val : Value] + +2avMessage == + (*************************************************************************) + (* When an acceptor receives a 1c message, it relays that message's *) + (* contents to the other acceptors in a 2av message. It does this only *) + (* for the first 1c message it receives for that ballot; it can receive *) + (* a second 1c message only if the leader is malicious, in which case it *) + (* ignores that second 1c message. *) + (*************************************************************************) + [type : {"2av"}, bal : Ballot, val : Value, acc : ByzAcceptor] + +2bMessage == [type : {"2b"}, acc : ByzAcceptor, bal : Ballot, val : Value] + (*************************************************************************) + (* 2b messages are the same as in ordinary Paxos. *) + (*************************************************************************) + +BMessage == + 1aMessage \cup 1bMessage \cup 1cMessage \cup 2avMessage \cup 2bMessage + +(***************************************************************************) +(* We will need the following simple fact about these sets of messages. *) +(***************************************************************************) +LEMMA BMessageLemma == + \A m \in BMessage : + /\ (m \in 1aMessage) <=> (m.type = "1a") + /\ (m \in 1bMessage) <=> (m.type = "1b") + /\ (m \in 1cMessage) <=> (m.type = "1c") + /\ (m \in 2avMessage) <=> (m.type = "2av") + /\ (m \in 2bMessage) <=> (m.type = "2b") +<1>1. /\ \A m \in 1aMessage : m.type = "1a" + /\ \A m \in 1bMessage : m.type = "1b" + /\ \A m \in 1cMessage : m.type = "1c" + /\ \A m \in 2avMessage : m.type = "2av" + /\ \A m \in 2bMessage : m.type = "2b" + BY DEF 1aMessage, 1bMessage, 1cMessage, 2avMessage, 2bMessage +<1>2. QED + BY <1>1 DEF BMessage +----------------------------------------------------------------------------- + + +(**************************************************************************** +We now give the algorithm. The basic idea is that the set Acceptor of +real acceptors emulate an execution of the PCon algorithm with +Acceptor as its set of acceptors. Of course, they must do that +without knowing which of the other processes in ByzAcceptor are real +acceptors and which are fake acceptors. In addition, they don't know +whether a leader is behaving according to the PCon algorithm or if it +is malicious. + +The main idea of the algorithm is that, before performing an action of +the PCon algorithm, a good acceptor determines that this action is +actually enabled in that algorithm. Since an action is enabled by the +receipt of one or more messages, the acceptor has to determine that +the enabling messages are legal PCon messages. Because algorithm PCon +allows a 1a message to be sent at any time, the only acceptor action +whose enabling messages must be checked is the Phase2b action. It is +enabled iff the appropriate 1c message and 2a message are legal. The +1c message is legal iff the leader has received the necessary 1b +messages. The acceptor therefore maintains a set of 1b messages that +it knows have been sent, and checks that those 1b messages enable the +sending of the 1c message. + +A 2a message is legal in the PCon algorithm iff (i) the corresponding +1c message is legal and (ii) it is the only 2a message that the leader +sends. In the BPCon algorithm, there are no explicit 2a messages. +They are implicitly sent by the acceptors when they send enough 2av +messages. + +We leave unspecified how an acceptor discovers what 1b messages have +been sent. In the Castro-Liskov algorithm, this is done by having +acceptors relay messages sent by other acceptors. An acceptor knows +that a 1b message has been sent if it receives it directly or else +receives a copy from a weak Byzantine quorum of acceptors. A +(non-malicious) leader must determine what 1b messages acceptors know +about so it chooses a value so that a quorum of acceptors will act on +its Phase1c message and cause that value to be chosen. However, this +is necessary only for liveness, so we ignore this for now. + +In other implementations of our algorithm, the leader sends along with +the 1c message a proof that the necessary 1b messages have been sent. +The easiest way to do this is to have acceptors digitally sign their 1b +messages, so a copy of the message proves that it has been sent (by the +acceptor indicated in the message's acc field). The necessary proofs +can also be constructed using only message authenticators (like the +ones used in the Castro-Liskov algorithm); how this is done is +described elsewhere. + +In the abstract algorithm presented here, which we call +BPCon, we do not specify how acceptors learn what 1b +messages have been sent. We simply introduce a variable knowsSent such +that knowsSent[a] represents the set of 1b messages that (good) +acceptor a knows have been sent, and have an action that +nondeterministically adds sent 1b messages to this set. + +--algorithm BPCon { + (************************************************************************** +The variables: + + maxBal[a] = Highest ballot in which acceptor a has participated. + + maxVBal[a] = Highest ballot in which acceptor a has cast a vote + (sent a 2b message); or -1 if it hasn't cast a vote. + + maxVVal[a] = Value acceptor a has voted for in ballot maxVBal[a], + or None if maxVBal[a] = -1. + + 2avSent[a] = A set of records in [val : Value, bal : Ballot] + describing the 2av messages that a has sent. A + record is added to this set, and any element with + the same val field (and lower bal field) removed + when a sends a 2av message. + + knownSent[a] = The set of 1b messages that acceptor a knows have + been sent. + + bmsgs = The set of all messages that have been sent. See the + discussion of the msgs variable in module PConProof + to understand our modeling of message passing. + **************************************************************************) + variables maxBal = [a \in Acceptor |-> -1], + maxVBal = [a \in Acceptor |-> -1] , + maxVVal = [a \in Acceptor |-> None] , + 2avSent = [a \in Acceptor |-> {}], + knowsSent = [a \in Acceptor |-> {}], + bmsgs = {} + define { + sentMsgs(type, bal) == {m \in bmsgs: m.type = type /\ m.bal = bal} + + KnowsSafeAt(ac, b, v) == + (*********************************************************************) + (* True for an acceptor ac, ballot b, and value v iff the set of 1b *) + (* messages in knowsSent[ac] implies that value v is safe at ballot *) + (* b in the PaxosConsensus algorithm being emulated by the good *) + (* acceptors. To understand the definition, see the definition of *) + (* ShowsSafeAt in module PConProof and recall (a) the meaning of the *) + (* mCBal and mCVal fields of a 1b message and (b) that the set of *) + (* real acceptors in a ByzQuorum forms a quorum of the *) + (* PaxosConsensus algorithm. *) + (*********************************************************************) + LET S == {m \in knowsSent[ac] : m.bal = b} + IN \/ \E BQ \in ByzQuorum : + \A a \in BQ : \E m \in S : /\ m.acc = a + /\ m.mbal = -1 + \/ \E c \in 0..(b-1): + /\ \E BQ \in ByzQuorum : + \A a \in BQ : \E m \in S : /\ m.acc = a + /\ m.mbal =< c + /\ (m.mbal = c) => (m.mval = v) + /\ \E WQ \in WeakQuorum : + \A a \in WQ : + \E m \in S : /\ m.acc = a + /\ \E r \in m.m2av : /\ r.bal >= c + /\ r.val = v + } + + (*************************************************************************) + (* We now describe the processes' actions as macros. *) + (* *) + (* The following two macros send a message and a set of messages, *) + (* respectively. These macros are so simple that they're hardly worth *) + (* introducing, but they do make the processes a little easier to read. *) + (*************************************************************************) + macro SendMessage(m) { bmsgs := bmsgs \cup {m} } + macro SendSetOfMessages(S) { bmsgs := bmsgs \cup S } + + (*************************************************************************) + (* As in the Paxos consensus algorithm, a ballot `self' leader (good or *) + (* malicious) can execute a Phase1a ation at any time. *) + (*************************************************************************) + macro Phase1a() { SendMessage([type |-> "1a", bal |-> self]) } + + (*************************************************************************) + (* The acceptor's Phase1b ation is similar to that of the PaxosConsensus *) + (* algorithm. *) + (*************************************************************************) + macro Phase1b(b) { + when (b > maxBal[self]) /\ (sentMsgs("1a", b) # {}) ; + maxBal[self] := b ; + SendMessage([type |-> "1b", bal |-> b, acc |-> self, m2av |-> 2avSent[self], + mbal |-> maxVBal[self], mval |-> maxVVal[self]]) + } + + (*************************************************************************) + (* A good ballot `self' leader can send a phase 1c message for value v *) + (* if it knows that the messages in knowsSent[a] for a Quorum of (good) *) + (* acceptors imply that they know that v is safe at ballot `self', and *) + (* that they can convince any other acceptor that the appropriate 1b *) + (* messages have been sent to that it will also know that v is safe at *) + (* ballot `self'. *) + (* *) + (* A malicious ballot `self' leader can send any phase 1c messages it *) + (* wants (including one that a good leader could send). We prove safety *) + (* with a Phase1c ation that allows a leader to be malicious. To prove *) + (* liveness, we will have to assume a good leader that sends only *) + (* correct 1c messages. *) + (* *) + (* As in the PaxosConsensus algorithm, we allow a Phase1c action to send *) + (* a set of Phase1c messages. (This is not done in the Castro-Liskov *) + (* algorithm, but seems natural in light of the PaxosConsensus *) + (* algorithm.) *) + (*************************************************************************) + macro Phase1c() { + with (S \in SUBSET [type : {"1c"}, bal : {self}, val : Value]) { + SendSetOfMessages(S) } + } + + (*************************************************************************) + (* If acceptor `self' receives a ballot b phase 1c message with value v, *) + (* it relays v in a phase 2av message if *) + (* *) + (* - it has not already sent a 2av message in this or a later *) + (* ballot and *) + (* *) + (* - the messages in knowsSent[self] show it that v is safe at b in *) + (* the non-Byzantine Paxos consensus algorithm being emulated. *) + (*************************************************************************) + macro Phase2av(b) { + when /\ maxBal[self] =< b + /\ \A r \in 2avSent[self] : r.bal < b ; + \* We could just as well have used r.bal # b in this condition. + with (m \in {ms \in sentMsgs("1c", b) : KnowsSafeAt(self, b, ms.val)}) { + SendMessage([type |-> "2av", bal |-> b, val |-> m.val, acc |-> self]) ; + 2avSent[self] := {r \in 2avSent[self] : r.val # m.val} + \cup {[val |-> m.val, bal |-> b]} + } ; + maxBal[self] := b ; + } + + (*************************************************************************) + (* Acceptor `self' can send a phase 2b message with value v if it has *) + (* received phase 2av messages from a Byzantine quorum, which implies *) + (* that a quorum of good acceptors assert that this is the first 1c *) + (* message sent by the leader and that the leader was allowed to send *) + (* that message. It sets maxBal[self], maxVBal[self], and maxVVal[self] *) + (* as in the non-Byzantine algorithm. *) + (*************************************************************************) + macro Phase2b(b) { + when maxBal[self] =< b ; + with (v \in {vv \in Value : + \E Q \in ByzQuorum : + \A aa \in Q : + \E m \in sentMsgs("2av", b) : /\ m.val = vv + /\ m.acc = aa} ) { + SendMessage([type |-> "2b", acc |-> self, bal |-> b, val |-> v]) ; + maxVVal[self] := v ; + } ; + maxBal[self] := b ; + maxVBal[self] := b + } + + (*************************************************************************) + (* At any time, an acceptor can learn that some set of 1b messages were *) + (* sent (but only if they atually were sent). *) + (*************************************************************************) + macro LearnsSent(b) { + with (S \in SUBSET sentMsgs("1b", b)) { + knowsSent[self] := knowsSent[self] \cup S + } + } + (*************************************************************************) + (* A malicious acceptor `self' can send any acceptor message indicating *) + (* that it is from itself. Since a malicious acceptor could allow other *) + (* malicious processes to forge its messages, this action could *) + (* represent the sending of the message by any malicious process. *) + (*************************************************************************) + macro FakingAcceptor() { + with ( m \in { mm \in 1bMessage \cup 2avMessage \cup 2bMessage : + mm.acc = self} ) { + SendMessage(m) + } + } + + (*************************************************************************) + (* We combine these individual actions into a complete algorithm in the *) + (* usual way, with separate process declarations for the acceptor, *) + (* leader, and fake acceptor processes. *) + (*************************************************************************) + process (acceptor \in Acceptor) { + acc: while (TRUE) { + with (b \in Ballot) {either Phase1b(b) or Phase2av(b) + or Phase2b(b) or LearnsSent(b)} + } + } + + process (leader \in Ballot) { + ldr: while (TRUE) { + either Phase1a() or Phase1c() + } + } + + process (facceptor \in FakeAcceptor) { + facc : while (TRUE) { FakingAcceptor() } + } +} + +Below is the TLA+ translation, as produced by the translator. (Some +blank lines have been removed.) +**************************************************************************) +\* BEGIN TRANSLATION +VARIABLES maxBal, maxVBal, maxVVal, 2avSent, knowsSent, bmsgs + +(* define statement *) +sentMsgs(type, bal) == {m \in bmsgs: m.type = type /\ m.bal = bal} + +KnowsSafeAt(ac, b, v) == + LET S == {m \in knowsSent[ac] : m.bal = b} + IN \/ \E BQ \in ByzQuorum : + \A a \in BQ : \E m \in S : /\ m.acc = a + /\ m.mbal = -1 + \/ \E c \in 0..(b-1): + /\ \E BQ \in ByzQuorum : + \A a \in BQ : \E m \in S : /\ m.acc = a + /\ m.mbal =< c + /\ (m.mbal = c) => (m.mval = v) + /\ \E WQ \in WeakQuorum : + \A a \in WQ : + \E m \in S : /\ m.acc = a + /\ \E r \in m.m2av : /\ r.bal >= c + /\ r.val = v + + +vars == << maxBal, maxVBal, maxVVal, 2avSent, knowsSent, bmsgs >> + +ProcSet == (Acceptor) \cup (Ballot) \cup (FakeAcceptor) + +Init == (* Global variables *) + /\ maxBal = [a \in Acceptor |-> -1] + /\ maxVBal = [a \in Acceptor |-> -1] + /\ maxVVal = [a \in Acceptor |-> None] + /\ 2avSent = [a \in Acceptor |-> {}] + /\ knowsSent = [a \in Acceptor |-> {}] + /\ bmsgs = {} + +acceptor(self) == \E b \in Ballot: + \/ /\ (b > maxBal[self]) /\ (sentMsgs("1a", b) # {}) + /\ maxBal' = [maxBal EXCEPT ![self] = b] + /\ bmsgs' = (bmsgs \cup {([type |-> "1b", bal |-> b, acc |-> self, m2av |-> 2avSent[self], + mbal |-> maxVBal[self], mval |-> maxVVal[self]])}) + /\ UNCHANGED <> + \/ /\ /\ maxBal[self] =< b + /\ \A r \in 2avSent[self] : r.bal < b + /\ \E m \in {ms \in sentMsgs("1c", b) : KnowsSafeAt(self, b, ms.val)}: + /\ bmsgs' = (bmsgs \cup {([type |-> "2av", bal |-> b, val |-> m.val, acc |-> self])}) + /\ 2avSent' = [2avSent EXCEPT ![self] = {r \in 2avSent[self] : r.val # m.val} + \cup {[val |-> m.val, bal |-> b]}] + /\ maxBal' = [maxBal EXCEPT ![self] = b] + /\ UNCHANGED <> + \/ /\ maxBal[self] =< b + /\ \E v \in {vv \in Value : + \E Q \in ByzQuorum : + \A aa \in Q : + \E m \in sentMsgs("2av", b) : /\ m.val = vv + /\ m.acc = aa}: + /\ bmsgs' = (bmsgs \cup {([type |-> "2b", acc |-> self, bal |-> b, val |-> v])}) + /\ maxVVal' = [maxVVal EXCEPT ![self] = v] + /\ maxBal' = [maxBal EXCEPT ![self] = b] + /\ maxVBal' = [maxVBal EXCEPT ![self] = b] + /\ UNCHANGED <<2avSent, knowsSent>> + \/ /\ \E S \in SUBSET sentMsgs("1b", b): + knowsSent' = [knowsSent EXCEPT ![self] = knowsSent[self] \cup S] + /\ UNCHANGED <> + + +leader(self) == /\ \/ /\ bmsgs' = (bmsgs \cup {([type |-> "1a", bal |-> self])}) + \/ /\ \E S \in SUBSET [type : {"1c"}, bal : {self}, val : Value]: + bmsgs' = (bmsgs \cup S) + /\ UNCHANGED << maxBal, maxVBal, maxVVal, 2avSent, knowsSent >> + + +facceptor(self) == /\ \E m \in { mm \in 1bMessage \cup 2avMessage \cup 2bMessage : + mm.acc = self}: + bmsgs' = (bmsgs \cup {m}) + /\ UNCHANGED << maxBal, maxVBal, maxVVal, 2avSent, + knowsSent >> + + +Next == (\E self \in Acceptor: acceptor(self)) + \/ (\E self \in Ballot: leader(self)) + \/ (\E self \in FakeAcceptor: facceptor(self)) + +Spec == Init /\ [][Next]_vars + +\* END TRANSLATION +----------------------------------------------------------------------------- +(***************************************************************************) +(* As in module PConProof, we now rewrite the next-state relation in a *) +(* form more convenient for writing proofs. *) +(***************************************************************************) +Phase1b(self, b) == + /\ (b > maxBal[self]) /\ (sentMsgs("1a", b) # {}) + /\ maxBal' = [maxBal EXCEPT ![self] = b] + /\ bmsgs' = bmsgs \cup {[type |-> "1b", bal |-> b, acc |-> self, + m2av |-> 2avSent[self], + mbal |-> maxVBal[self], mval |-> maxVVal[self]]} + /\ UNCHANGED <> + +Phase2av(self, b) == + /\ maxBal[self] =< b + /\ \A r \in 2avSent[self] : r.bal < b + /\ \E m \in {ms \in sentMsgs("1c", b) : KnowsSafeAt(self, b, ms.val)}: + /\ bmsgs' = bmsgs \cup + {[type |-> "2av", bal |-> b, val |-> m.val, acc |-> self]} + /\ 2avSent' = [2avSent EXCEPT + ![self] = {r \in 2avSent[self] : r.val # m.val} + \cup {[val |-> m.val, bal |-> b]}] + /\ maxBal' = [maxBal EXCEPT ![self] = b] + /\ UNCHANGED <> + +Phase2b(self, b) == + /\ maxBal[self] =< b + /\ \E v \in {vv \in Value : + \E Q \in ByzQuorum : + \A a \in Q : + \E m \in sentMsgs("2av", b) : /\ m.val = vv + /\ m.acc = a }: + /\ bmsgs' = (bmsgs \cup + {[type |-> "2b", acc |-> self, bal |-> b, val |-> v]}) + /\ maxVVal' = [maxVVal EXCEPT ![self] = v] + /\ maxBal' = [maxBal EXCEPT ![self] = b] + /\ maxVBal' = [maxVBal EXCEPT ![self] = b] + /\ UNCHANGED <<2avSent, knowsSent>> + +LearnsSent(self, b) == + /\ \E S \in SUBSET sentMsgs("1b", b): + knowsSent' = [knowsSent EXCEPT ![self] = knowsSent[self] \cup S] + /\ UNCHANGED <> + +Phase1a(self) == + /\ bmsgs' = (bmsgs \cup {[type |-> "1a", bal |-> self]}) + /\ UNCHANGED << maxBal, maxVBal, maxVVal, 2avSent, knowsSent >> + +Phase1c(self) == + /\ \E S \in SUBSET [type : {"1c"}, bal : {self}, val : Value]: + bmsgs' = (bmsgs \cup S) + /\ UNCHANGED << maxBal, maxVBal, maxVVal, 2avSent, knowsSent >> + +FakingAcceptor(self) == + /\ \E m \in { mm \in 1bMessage \cup 2avMessage \cup 2bMessage : mm.acc = self} : + bmsgs' = (bmsgs \cup {m}) + /\ UNCHANGED << maxBal, maxVBal, maxVVal, 2avSent, knowsSent >> +----------------------------------------------------------------------------- +(***************************************************************************) +(* The following lemma describes how the next-state relation Next can be *) +(* written in terms of the actions defined above. *) +(***************************************************************************) +LEMMA NextDef == + Next <=> \/ \E self \in Acceptor : + \E b \in Ballot : \/ Phase1b(self, b) + \/ Phase2av(self, b) + \/ Phase2b(self,b) + \/ LearnsSent(self, b) + \/ \E self \in Ballot : \/ Phase1a(self) + \/ Phase1c(self) + \/ \E self \in FakeAcceptor : FakingAcceptor(self) +<1>1. \A self : acceptor(self) <=> NextDef!2!1!(self) + BY DEF acceptor, Phase1b, Phase2av, Phase2b, LearnsSent +<1>2. \A self : leader(self) <=> NextDef!2!2!(self) + BY DEF leader, Phase1a, Phase1c +<1>3. \A self : facceptor(self) <=> NextDef!2!3!(self) + BY DEF facceptor, FakingAcceptor +<1>4. QED + BY <1>1, <1>2, <1>3, Zenon + DEF Next, acceptor, leader, facceptor +----------------------------------------------------------------------------- +(***************************************************************************) +(* THE REFINEMENT MAPPING *) +(***************************************************************************) + +(***************************************************************************) +(* We define a quorum to be the set of acceptors in a Byzantine quorum. *) +(* The quorum assumption QA of module PConProof, which we here call *) +(* QuorumTheorem, follows easily from the definition and assumption BQA. *) +(***************************************************************************) +Quorum == {S \cap Acceptor : S \in ByzQuorum} + +THEOREM QuorumTheorem == + /\ \A Q1, Q2 \in Quorum : Q1 \cap Q2 # {} + /\ \A Q \in Quorum : Q \subseteq Acceptor +BY BQA DEF Quorum + +(***************************************************************************) +(* We now define refinement mapping under which our algorithm implements *) +(* the algorithm of module PConProof. First, we define the set msgs that *) +(* implements the variable of the same name in PConProof. There are two *) +(* non-obvious parts of the definition. *) +(* *) +(* 1. The 1c messages in msgs should just be the ones that are *) +(* legal--that is, messages whose value is safe at the indicated ballot. *) +(* The obvious way to define legality is in terms of 1b messages that have *) +(* been sent. However, this has the effect that sending a 1b message can *) +(* add both that 1b message and one or more 1c messages to msgs. Proving *) +(* implementation under this refinement mapping would require adding a *) +(* stuttering variable. Instead, we define the 1c message to be legal if *) +(* the set of 1b messages that some acceptor knows were sent confirms its *) +(* legality. Thus, those 1c messages are added to msgs by the LearnsSent *) +(* ation, which has no other effect on the refinement mapping. *) +(* *) +(* 2. A 2a message is added to msgs when a quorum of acceptors have *) +(* reacted to it by sending a 2av message. *) +(***************************************************************************) +msgsOfType(t) == {m \in bmsgs : m.type = t } + +acceptorMsgsOfType(t) == {m \in msgsOfType(t) : m.acc \in Acceptor} + +1bRestrict(m) == [type |-> "1b", acc |-> m.acc, bal |-> m.bal, + mbal |-> m.mbal, mval |-> m.mval] + +1bmsgs == { 1bRestrict(m) : m \in acceptorMsgsOfType("1b") } + +1cmsgs == {m \in msgsOfType("1c") : + \E a \in Acceptor : KnowsSafeAt(a, m.bal, m.val)} + +2amsgs == {m \in [type : {"2a"}, bal : Ballot, val : Value] : + \E Q \in Quorum : + \A a \in Q : + \E m2av \in acceptorMsgsOfType("2av") : + /\ m2av.acc = a + /\ m2av.bal = m.bal + /\ m2av.val = m.val } + +msgs == msgsOfType("1a") \cup 1bmsgs \cup 1cmsgs \cup 2amsgs + \cup acceptorMsgsOfType("2b") + +(***************************************************************************) +(* We now define PmaxBal, the state function with which we instantiate the *) +(* variable maxBal of PConProof. The reason we don't just instantiate it *) +(* with the variable maxBal is that maxBal[a] can change when acceptor `a' *) +(* performs a Phase2av ation, which does not correspond to any acceptor *) +(* action of the PCon algorithm. We want PmaxBal[a] to change only *) +(* when `a' performs a Phase1b or Phase2b ation--that is, when it sends a *) +(* 1b or 2b message. Thus, we define PmaxBal[a] to be the largest bal *) +(* field of all 1b and 2b messages sent by `a'. *) +(* *) +(* To define PmaxBal, we need to define an operator MaxBallot so that *) +(* MaxBallot(S) is the largest element of S if S is non-empty a finite set *) +(* consisting of ballot numbers and possibly the value -1. *) +(***************************************************************************) +MaxBallot(S) == + IF S = {} THEN -1 + ELSE CHOOSE mb \in S : \A x \in S : mb >= x + +(***************************************************************************) +(* To prove that the CHOOSE in this definition actually does choose a *) +(* maximum of S when S is nonempty, we need the following fact. *) +(***************************************************************************) +LEMMA FiniteSetHasMax == + \A S \in SUBSET Int : + IsFiniteSet(S) /\ (S # {}) => \E max \in S : \A x \in S : max >= x +<1>. DEFINE P(S) == S \subseteq Int /\ S # {} => + \E max \in S : \A x \in S : max >= x +<1>1. P({}) + OBVIOUS +<1>2. ASSUME NEW T, NEW x, P(T) + PROVE P(T \cup {x}) + BY <1>2 +<1>3. \A S : IsFiniteSet(S) => P(S) + <2>. HIDE DEF P + <2>. QED BY <1>1, <1>2, FS_Induction, IsaM("blast") +<1>. QED BY <1>3, Zenon + +(***************************************************************************) +(* Our proofs use this property of MaxBallot. *) +(***************************************************************************) +THEOREM MaxBallotProp == + ASSUME NEW S \in SUBSET (Ballot \cup {-1}), + IsFiniteSet(S) + PROVE IF S = {} THEN MaxBallot(S) = -1 + ELSE /\ MaxBallot(S) \in S + /\ \A x \in S : MaxBallot(S) >= x +<1>1. CASE S = {} + BY <1>1 DEF MaxBallot +<1>2. CASE S # {} + <2>. PICK mb \in S : \A x \in S : mb >= x + BY <1>2, FiniteSetHasMax DEF Ballot + <2>. QED BY <1>2 DEF MaxBallot +<1>. QED BY <1>1, <1>2 + +(***************************************************************************) +(* We now prove a couple of lemmas about MaxBallot. *) +(***************************************************************************) +LEMMA MaxBallotLemma1 == + ASSUME NEW S \in SUBSET (Ballot \cup {-1}), + IsFiniteSet(S), + NEW y \in S, \A x \in S : y >= x + PROVE y = MaxBallot(S) +<1>1. /\ MaxBallot(S) \in S + /\ MaxBallot(S) >= y + BY MaxBallotProp +<1>2 /\ y \in Ballot \cup {-1} + /\ y >= MaxBallot(S) + BY MaxBallotProp +<1>3. MaxBallot(S) \in Int /\ y \in Int + BY <1>1, <1>2, Isa DEF Ballot +<1>. QED BY <1>1, <1>2, <1>3 + +LEMMA MaxBallotLemma2 == + ASSUME NEW S \in SUBSET (Ballot \cup {-1}), + NEW T \in SUBSET (Ballot \cup {-1}), + IsFiniteSet(S), IsFiniteSet(T) + PROVE MaxBallot(S \cup T) = IF MaxBallot(S) >= MaxBallot(T) + THEN MaxBallot(S) ELSE MaxBallot(T) +<1>1. /\ MaxBallot(S) \in Ballot \cup {-1} + /\ MaxBallot(T) \in Ballot \cup {-1} + BY MaxBallotProp +<1>. S \cup T \subseteq Int + BY DEF Ballot +<1>2. CASE MaxBallot(S) >= MaxBallot(T) + <2>. SUFFICES ASSUME T # {} + PROVE MaxBallot(S \cup T) = MaxBallot(S) + BY <1>2, Zenon + <2>1. /\ MaxBallot(T) \in T + /\ \A x \in T : MaxBallot(T) >= x + BY MaxBallotProp + <2>2. CASE S = {} + <3>1. MaxBallot(S) = -1 + BY <2>2 DEF MaxBallot + <3>2. MaxBallot(T) = -1 + BY <3>1, <1>2, <1>1 DEF Ballot + <3>. QED BY <2>2, <3>1, <3>2, <2>1, MaxBallotLemma1, FS_Union + <2>3. CASE S # {} + <3>1. /\ MaxBallot(S) \in S + /\ \A x \in S : MaxBallot(S) >= x + BY <2>3, MaxBallotProp + <3>2. /\ MaxBallot(S) \in S \cup T + /\ \A x \in S \cup T : MaxBallot(S) >= x + BY <3>1, <2>1, <1>2 + <3>. QED BY <3>2, MaxBallotLemma1, FS_Union, Zenon + <2>. QED BY <2>2, <2>3 +<1>3. CASE ~(MaxBallot(S) >= MaxBallot(T)) + <2>. SUFFICES ASSUME S # {} + PROVE MaxBallot(S \cup T) = MaxBallot(T) + BY <1>3 + <2>1. /\ MaxBallot(S) \in S + /\ \A x \in S : MaxBallot(S) >= x + BY MaxBallotProp + <2>2. /\ MaxBallot(S) < MaxBallot(T) + /\ MaxBallot(T) # -1 + BY <1>3, <1>1 DEF Ballot + <2>3. /\ MaxBallot(T) \in T + /\ \A x \in T : MaxBallot(T) >= x + BY <2>2, MaxBallotProp + <2>4. /\ MaxBallot(T) \in S \cup T + /\ \A x \in S \cup T : MaxBallot(T) >= x + BY <2>3, <2>2, <2>1 + <2>. QED BY <2>4, MaxBallotLemma1, FS_Union, Zenon +<1>. QED BY <1>2, <1>3 + + +(***************************************************************************) +(* We finally come to our definition of PmaxBal, the state function *) +(* substituted for variable maxBal of module PConProof by our refinement *) +(* mapping. We also prove a couple of lemmas about PmaxBal. *) +(***************************************************************************) + +1bOr2bMsgs == {m \in bmsgs : m.type \in {"1b", "2b"}} + +PmaxBal == [a \in Acceptor |-> + MaxBallot({m.bal : m \in {ma \in 1bOr2bMsgs : + ma.acc = a}})] + +LEMMA PmaxBalLemma1 == + ASSUME NEW m , + bmsgs' = bmsgs \cup {m}, + m.type # "1b" /\ m.type # "2b" + PROVE PmaxBal' = PmaxBal +BY Zenon DEF PmaxBal, 1bOr2bMsgs + +LEMMA PmaxBalLemma2 == + ASSUME NEW m, + bmsgs' = bmsgs \cup {m}, + NEW a \in Acceptor, + m.acc # a + PROVE PmaxBal'[a] = PmaxBal[a] +BY DEF PmaxBal, 1bOr2bMsgs + +(***************************************************************************) +(* Finally, we define the refinement mapping. As before, for any operator *) +(* op defined in module PConProof, the following INSTANCE statement *) +(* defines P!op to be the operator obtained from op by the indicated *) +(* substitutions, along with the implicit substitutions *) +(* *) +(* Acceptor <- Acceptor, *) +(* Quorum <- Quorum *) +(* Value <- Value *) +(* maxVBal <- maxVBal *) +(* maxVVal <- maxVVal *) +(* msgs <- msgs *) +(***************************************************************************) +P == INSTANCE PConProof WITH maxBal <- PmaxBal +----------------------------------------------------------------------------- +(***************************************************************************) +(* We now define the inductive invariant Inv used in our proof. It is *) +(* defined to be the conjunction of a number of separate invariants that *) +(* we define first, starting with the ever-present type-correctness *) +(* invariant. *) +(***************************************************************************) +TypeOK == /\ maxBal \in [Acceptor -> Ballot \cup {-1}] + /\ 2avSent \in [Acceptor -> SUBSET [val : Value, bal : Ballot]] + /\ maxVBal \in [Acceptor -> Ballot \cup {-1}] + /\ maxVVal \in [Acceptor -> Value \cup {None}] + /\ knowsSent \in [Acceptor -> SUBSET 1bMessage] + /\ bmsgs \subseteq BMessage + +(***************************************************************************) +(* To use the definition of PmaxBal, we need to know that the set of 1b *) +(* and 2b messages in bmsgs is finite. This is asserted by the following *) +(* invariant. Note that the set bmsgs is not necessarily finite because *) +(* we allow a Phase1c action to send an infinite number of 1c messages. *) +(***************************************************************************) +bmsgsFinite == IsFiniteSet(1bOr2bMsgs) + +(***************************************************************************) +(* The following lemma is used to prove the invariance of bmsgsFinite. *) +(***************************************************************************) +LEMMA FiniteMsgsLemma == + ASSUME NEW m, bmsgsFinite, bmsgs' = bmsgs \cup {m} + PROVE bmsgsFinite' +BY FS_AddElement DEF bmsgsFinite, 1bOr2bMsgs + +(***************************************************************************) +(* Invariant 1bInv1 asserts that if (good) acceptor `a' has mCBal[a] # -1, *) +(* then there is a 1c message for ballot mCBal[a] and value mCVal[a] in *) +(* the emulated execution of algorithm PCon. *) +(***************************************************************************) +1bInv1 == \A m \in bmsgs : + /\ m.type = "1b" + /\ m.acc \in Acceptor + => \A r \in m.m2av : + [type |-> "1c", bal |-> r.bal, val |-> r.val] \in msgs + +(***************************************************************************) +(* Invariant 1bInv2 asserts that an acceptor sends at most one 1b message *) +(* for any ballot. *) +(***************************************************************************) +1bInv2 == \A m1, m2 \in bmsgs : + /\ m1.type = "1b" + /\ m2.type = "1b" + /\ m1.acc \in Acceptor + /\ m1.acc = m2.acc + /\ m1.bal = m2.bal + => m1 = m2 + +(***************************************************************************) +(* Invariant 2avInv1 asserts that an acceptor sends at most one 2av *) +(* message in any ballot. *) +(***************************************************************************) +2avInv1 == \A m1, m2 \in bmsgs : + /\ m1.type = "2av" + /\ m2.type = "2av" + /\ m1.acc \in Acceptor + /\ m1.acc = m2.acc + /\ m1.bal = m2.bal + => m1 = m2 + +(***************************************************************************) +(* Invariant 2avInv2 follows easily from the meaning (and setting) of *) +(* 2avSent. *) +(***************************************************************************) +2avInv2 == \A m \in bmsgs : + /\ m.type = "2av" + /\ m.acc \in Acceptor + => \E r \in 2avSent[m.acc] : /\ r.val = m.val + /\ r.bal >= m.bal + +(***************************************************************************) +(* Invariant 2avInv3 asserts that an acceptor sends a 2av message only if *) +(* the required 1c message exists in the emulated execution of *) +(* algorithm PConf. *) +(***************************************************************************) +2avInv3 == \A m \in bmsgs : + /\ m.type = "2av" + /\ m.acc \in Acceptor + => [type |-> "1c", bal |-> m.bal, val |-> m.val] \in msgs + +(***************************************************************************) +(* Invariant maxBalInv is a simple consequence of the fact that an *) +(* acceptor `a' sets maxBal[a] to b whenever it sends a 1b, 2av, or 2b *) +(* message in ballot b. *) +(***************************************************************************) +maxBalInv == \A m \in bmsgs : + /\ m.type \in {"1b", "2av", "2b"} + /\ m.acc \in Acceptor + => m.bal =< maxBal[m.acc] + +(***************************************************************************) +(* Invariant accInv asserts some simple relations between the variables *) +(* local to an acceptor, as well as the fact that acceptor `a' sets *) +(* maxCBal[a] to b and maxCVal[a] to v only if there is a ballot-b 1c *) +(* message for value c in the simulated execution of the PCon *) +(* algorithm. *) +(***************************************************************************) +accInv == \A a \in Acceptor : + \A r \in 2avSent[a] : + /\ r.bal =< maxBal[a] + /\ [type |-> "1c", bal |-> r.bal, val |-> r.val] \in msgs + +(***************************************************************************) +(* Invariant knowsSentInv simply asserts that for any acceptor `a', *) +(* knowsSent[a] is a set of 1b messages that have actually been sent. *) +(***************************************************************************) +knowsSentInv == \A a \in Acceptor : knowsSent[a] \subseteq msgsOfType("1b") + +Inv == + TypeOK /\ bmsgsFinite /\ 1bInv1 /\ 1bInv2 /\ maxBalInv /\ 2avInv1 /\ 2avInv2 + /\ 2avInv3 /\ accInv /\ knowsSentInv +----------------------------------------------------------------------------- +(***************************************************************************) +(* We now prove some simple lemmas that are useful for reasoning about *) +(* PmaxBal. *) +(***************************************************************************) +LEMMA PMaxBalLemma3 == + ASSUME TypeOK, + bmsgsFinite, + NEW a \in Acceptor + PROVE LET S == {m.bal : m \in {ma \in bmsgs : + /\ ma.type \in {"1b", "2b"} + /\ ma.acc = a}} + IN /\ IsFiniteSet(S) + /\ S \in SUBSET Ballot +<1> DEFINE T == {ma \in bmsgs : /\ ma.type \in {"1b", "2b"} + /\ ma.acc = a} + S == {m.bal : m \in T} +<1>1. IsFiniteSet(S) + <2>1. IsFiniteSet(T) + BY FS_Subset DEF bmsgsFinite, 1bOr2bMsgs + <2>. QED + BY <2>1, FS_Image, Isa +<1>. QED BY <1>1, BMessageLemma DEF 1bMessage, 2bMessage, TypeOK + +LEMMA PmaxBalLemma4 == + ASSUME TypeOK, + maxBalInv, + bmsgsFinite, + NEW a \in Acceptor + PROVE PmaxBal[a] =< maxBal[a] +<1> DEFINE SM == {ma \in bmsgs : /\ ma.type \in {"1b", "2b"} + /\ ma.acc = a} + S == {ma.bal : ma \in SM} +<1>1. PmaxBal[a] = MaxBallot(S) + BY DEF PmaxBal, 1bOr2bMsgs +<1>2. /\ IsFiniteSet(S) + /\ S \in SUBSET Ballot + BY PMaxBalLemma3 +<1>3. \A b \in S : b =< maxBal[a] + BY DEF maxBalInv +<1>4. CASE S = {} + <2>1. PmaxBal[a] = -1 + BY <1>2, <1>1, <1>4, MaxBallotProp + <2>. QED + BY <2>1 DEF Ballot, TypeOK +<1>5. CASE S # {} + <2>1. MaxBallot(S) \in S + BY <1>2, <1>5, MaxBallotProp, Zenon + <2>2. QED + BY <1>1, <1>3, <2>1 +<1>6. QED + BY <1>4, <1>5 + +LEMMA PmaxBalLemma5 == + ASSUME TypeOK, bmsgsFinite, NEW a \in Acceptor + PROVE PmaxBal[a] \in Ballot \cup {-1} +BY PMaxBalLemma3, MaxBallotProp DEF PmaxBal, 1bOr2bMsgs + +----------------------------------------------------------------------------- +(***************************************************************************) +(* Now comes a bunch of useful lemmas. *) +(***************************************************************************) + +(***************************************************************************) +(* We first prove that P!NextDef is a valid theorem and give it the name *) +(* PNextDef. This requires proving that the assumptions of module *) +(* PConProof are satisfied by the refinement mapping. Note that *) +(* P!NextDef!: is an abbreviation for the statement of theorem P!NextDef *) +(* -- that is, for the statement of theorem NextDef of module PConProof *) +(* under the substitutions of the refinement mapping. *) +(***************************************************************************) +LEMMA PNextDef == P!NextDef!: +<1>1. P!QA + BY QuorumTheorem +<1>2. P!BallotAssump + BY BallotAssump DEF Ballot, P!Ballot, ByzAcceptor +<1>3. QED + BY P!NextDef, <1>1, <1>2, NoSetContainsEverything + +(***************************************************************************) +(* For convenience, we define operators corresponding to subexpressions *) +(* that appear in the definition of KnowsSafeAt. *) +(***************************************************************************) +KSet(a, b) == {m \in knowsSent[a] : m.bal = b} +KS1(S) == \E BQ \in ByzQuorum : \A a \in BQ : + \E m \in S : m.acc = a /\ m.mbal = -1 +KS2(v,b,S) == \E c \in 0 .. (b-1) : + /\ \E BQ \in ByzQuorum : \A a \in BQ : + \E m \in S : /\ m.acc = a + /\ m.mbal =< c + /\ (m.mbal = c) => (m.mval = v) + /\ \E WQ \in WeakQuorum : \A a \in WQ : + \E m \in S : /\ m.acc = a + /\ \E r \in m.m2av : /\ r.bal >= c + /\ r.val = v + +(***************************************************************************) +(* The following lemma asserts the obvious relation between KnowsSafeAt *) +(* and the top-level definitions KS1, KS2, and KSet. The second conjunct *) +(* is, of course, the primed version of the first. *) +(***************************************************************************) +LEMMA KnowsSafeAtDef == + \A a, b, v : + /\ KnowsSafeAt(a, b, v) <=> KS1(KSet(a,b)) \/ KS2(v, b, KSet(a, b)) + /\ KnowsSafeAt(a, b, v)' <=> KS1(KSet(a,b)') \/ KS2(v, b, KSet(a, b)') + BY DEF KnowsSafeAt, KSet, KS1, KS2 + +LEMMA MsgsTypeLemma == + \A m \in msgs : /\ (m.type = "1a") <=> (m \in msgsOfType("1a")) + /\ (m.type = "1b") <=> (m \in 1bmsgs) + /\ (m.type = "1c") <=> (m \in 1cmsgs) + /\ (m.type = "2a") <=> (m \in 2amsgs) + /\ (m.type = "2b") <=> (m \in acceptorMsgsOfType("2b")) +BY DEF msgsOfType, 1bmsgs, 1bRestrict, 1cmsgs, 2amsgs, acceptorMsgsOfType, msgs + +(***************************************************************************) +(* The following lemma is the primed version of MsgsTypeLemma. That is, *) +(* its statement is just the statement of MsgsTypeLemma primed. It *) +(* follows from MsgsTypeLemma by the meta-theorem that if we can prove a *) +(* state-predicate F as a (top-level) theorem, then we can deduce F'. This *) +(* is an instance of propositional temporal-logic reasoning. Alternatively *) +(* the lemma could be proved using the same reasoning used for the *) +(* unprimed version of the theorem. *) +(***************************************************************************) +LEMMA MsgsTypeLemmaPrime == + \A m \in msgs' : /\ (m.type = "1a") <=> (m \in msgsOfType("1a")') + /\ (m.type = "1b") <=> (m \in 1bmsgs') + /\ (m.type = "1c") <=> (m \in 1cmsgs') + /\ (m.type = "2a") <=> (m \in 2amsgs') + /\ (m.type = "2b") <=> (m \in acceptorMsgsOfType("2b")') +<1>1. MsgsTypeLemma' + BY MsgsTypeLemma, PTL +<1>. QED + BY <1>1 + +(***************************************************************************) +(* The following lemma describes how msgs is changed by the actions of the *) +(* algorithm. *) +(***************************************************************************) +LEMMA MsgsLemma == +TypeOK => + /\ \A self \in Acceptor, b \in Ballot : + Phase1b(self, b) => + msgs' = msgs \cup + {[type |-> "1b", acc |-> self, bal |-> b, + mbal |-> maxVBal[self], mval |-> maxVVal[self]]} + /\ \A self \in Acceptor, b \in Ballot : + Phase2av(self, b) => + \/ msgs' = msgs + \/ \E v \in Value : + /\ [type |-> "1c", bal |-> b, val |-> v] \in msgs + /\ msgs' = msgs \cup {[type |-> "2a", bal |-> b, val |-> v]} + /\ \A self \in Acceptor, b \in Ballot : + Phase2b(self, b) => + \E v \in Value : + /\ \E Q \in ByzQuorum : + \A a \in Q : + \E m \in sentMsgs("2av", b) : /\ m.val = v + /\ m.acc = a + /\ msgs' = msgs \cup + {[type |-> "2b", acc |-> self, bal |-> b, val |-> v]} + /\ bmsgs' = bmsgs \cup + {[type |-> "2b", acc |-> self, bal |-> b, val |-> v]} + /\ maxVVal' = [maxVVal EXCEPT ![self] = v] + /\ \A self \in Acceptor, b \in Ballot : + LearnsSent(self, b) => + \E S \in SUBSET {m \in msgsOfType("1c") : m.bal = b} : + msgs' = msgs \cup S + /\ \A self \in Ballot : + Phase1a(self) => + msgs' = msgs \cup {[type |-> "1a", bal |-> self]} + /\ \A self \in Ballot : + Phase1c(self) => + \E S \in SUBSET [type : {"1c"}, bal : {self}, val : Value]: + /\ \A m \in S : + \E a \in Acceptor : KnowsSafeAt(a, m.bal, m.val) + /\ msgs' = msgs \cup S + /\ \A self \in FakeAcceptor : FakingAcceptor(self) => msgs' = msgs +<1> HAVE TypeOK + +<1>1. ASSUME NEW self \in Acceptor, NEW b \in Ballot, Phase1b(self,b) + PROVE msgs' = msgs \cup + {[type |-> "1b", acc |-> self, bal |-> b, + mbal |-> maxVBal[self], mval |-> maxVVal[self]]} + <2> DEFINE m == [type |-> "1b", acc |-> self, bal |-> b, + m2av |-> 2avSent[self], + mbal |-> maxVBal[self], mval |-> maxVVal[self]] + <2>1. bmsgs' = bmsgs \cup {m} /\ knowsSent' = knowsSent + BY <1>1 DEF Phase1b + <2>a. /\ msgsOfType("1a")' = msgsOfType("1a") + /\ 1bmsgs' = 1bmsgs \cup {1bRestrict(m)} + /\ 1cmsgs' = 1cmsgs + /\ 2amsgs' = 2amsgs + /\ acceptorMsgsOfType("2b")' = acceptorMsgsOfType("2b") + BY <2>1 DEF msgsOfType, 1bmsgs, acceptorMsgsOfType, KnowsSafeAt, 1cmsgs, 2amsgs + <2>. QED + BY <2>a DEF msgs, 1bRestrict + +<1>2. ASSUME NEW self \in Acceptor, NEW b \in Ballot, Phase2av(self,b) + PROVE \/ msgs' = msgs + \/ \E v \in Value : + /\ [type |-> "1c", bal |-> b, val |-> v] \in msgs + /\ msgs' = msgs \cup {[type |-> "2a", bal |-> b, val |-> v]} + <2>1. PICK m \in sentMsgs("1c", b) : + /\ KnowsSafeAt(self, b, m.val) + /\ bmsgs' = bmsgs \cup + {[type |-> "2av", bal |-> b, val |-> m.val, acc |-> self]} + BY <1>2 DEF Phase2av + <2>2. m = [type |-> "1c", bal |-> b, val |-> m.val] + BY BMessageLemma DEF sentMsgs, TypeOK, 1cMessage + <2> DEFINE ma == [type |-> "2a", bal |-> b, val |-> m.val] + mb == [type |-> "2av", bal |-> b, val |-> m.val, acc |-> self] + <2>3. SUFFICES ASSUME msgs' # msgs + PROVE /\ m \in msgs + /\ msgs' = msgs \cup {ma} + BY <2>2, BMessageLemma DEF sentMsgs, TypeOK, 1cMessage + <2>4. m \in msgs + BY <2>1, <2>2 DEF sentMsgs, 1cmsgs, msgsOfType, msgs + <2>5. msgs' = msgs \cup {ma} + <3>1. knowsSent' = knowsSent + BY <1>2 DEF Phase2av + <3>2. /\ msgsOfType("1a")' = msgsOfType("1a") + /\ 1bmsgs' = 1bmsgs + /\ 1cmsgs' = 1cmsgs + /\ acceptorMsgsOfType("2b")' = acceptorMsgsOfType("2b") + BY <2>1, <3>1 DEF msgsOfType, 1bmsgs, 1bRestrict, acceptorMsgsOfType, KnowsSafeAt, 1cmsgs + <3>. QED + BY <3>1, <3>2, <2>1, <2>3 DEF msgs, 2amsgs, msgsOfType, acceptorMsgsOfType + <2>6. QED + BY <2>4, <2>5 + +<1>3. ASSUME NEW self \in Acceptor, NEW b \in Ballot, Phase2b(self, b) + PROVE \E v \in Value : + /\ \E Q \in ByzQuorum : + \A a \in Q : + \E m \in sentMsgs("2av", b) : /\ m.val = v + /\ m.acc = a + /\ msgs' = msgs \cup + {[type |-> "2b", acc |-> self, bal |-> b, val |-> v]} + /\ bmsgs' = bmsgs \cup + {[type |-> "2b", acc |-> self, bal |-> b, val |-> v]} + /\ maxVVal' = [maxVVal EXCEPT ![self] = v] + <2>1. PICK v \in Value : + /\ \E Q \in ByzQuorum : + \A a \in Q : + \E m \in sentMsgs("2av", b) : /\ m.val = v + /\ m.acc = a + /\ bmsgs' = bmsgs \cup + {[type |-> "2b", acc |-> self, bal |-> b, val |-> v]} + /\ maxVVal' = [maxVVal EXCEPT ![self] = v] + /\ knowsSent' = knowsSent + BY <1>3, Zenon DEF Phase2b + <2> DEFINE bm == [type |-> "2b", acc |-> self, bal |-> b, val |-> v] + <2>2. /\ msgsOfType("1a")' = msgsOfType("1a") + /\ 1bmsgs' = 1bmsgs + /\ 1cmsgs' = 1cmsgs + /\ 2amsgs' = 2amsgs + /\ acceptorMsgsOfType("2b")' = acceptorMsgsOfType("2b") \cup {bm} + BY <2>1 DEF msgsOfType, 1bmsgs, 1bRestrict, 1cmsgs, KnowsSafeAt, 2amsgs, acceptorMsgsOfType + <2>4. msgs' = msgs \cup {bm} + BY <2>2 DEF msgs + <2>. QED + BY <2>1, <2>4, Zenon + +<1>4. ASSUME NEW self \in Acceptor, NEW b \in Ballot, LearnsSent(self, b) + PROVE \E S \in SUBSET {m \in msgsOfType("1c") : m.bal = b} : msgs' = msgs \cup S + <2>1. /\ msgsOfType("1a")' = msgsOfType("1a") + /\ 1bmsgs' = 1bmsgs + /\ 2amsgs' = 2amsgs + /\ acceptorMsgsOfType("2b")' = acceptorMsgsOfType("2b") + BY <1>4 DEF LearnsSent, msgsOfType, 1bmsgs, 1bRestrict, 2amsgs, acceptorMsgsOfType + <2>. /\ 1cmsgs \subseteq 1cmsgs' + /\ 1cmsgs' \ 1cmsgs \in SUBSET {m \in msgsOfType("1c") : m.bal = b} + <3>1. bmsgs' = bmsgs + BY <1>4 DEF LearnsSent + <3>2. PICK S \in SUBSET sentMsgs("1b", b): + knowsSent' = [knowsSent EXCEPT ![self] = knowsSent[self] \cup S] + BY <1>4, Zenon DEF LearnsSent + <3>3. ASSUME NEW m \in 1cmsgs + PROVE m \in 1cmsgs' + BY <3>1, <3>2 DEF TypeOK, KnowsSafeAt, 1cmsgs, msgsOfType + <3>4. ASSUME NEW m \in 1cmsgs', m \notin 1cmsgs + PROVE m \in msgsOfType("1c") /\ m.bal = b + <4>1. m \in msgsOfType("1c") + BY <3>1 DEF 1cmsgs, msgsOfType + <4>2. PICK a \in Acceptor : KnowsSafeAt(a, m.bal, m.val)' + BY DEF 1cmsgs + <4>3. ~KnowsSafeAt(a, m.bal, m.val) + BY <3>4, <4>1 DEF 1cmsgs + <4>4. \A aa \in Acceptor, bb \in Ballot : + \A mm \in KSet(aa, bb)' : + mm \notin KSet(aa, bb) => bb = b + BY <1>4, <3>2 DEF TypeOK, LearnsSent, TypeOK, sentMsgs, KSet + <4>5. m.bal \in Ballot + BY <4>1, BMessageLemma DEF 1cMessage, msgsOfType, TypeOK + <4>6. CASE KS1(KSet(a,m.bal)') /\ ~KS1(KSet(a,m.bal)) + BY <4>6, <4>1, <4>4, <4>5 DEF KS1 + <4>7. CASE KS2(m.val, m.bal, KSet(a, m.bal)') /\ ~KS2(m.val, m.bal, KSet(a, m.bal)) + BY <4>7, <4>1, <4>4, <4>5 DEF KS2 + <4> QED + BY <4>6, <4>7, <4>2, <4>3, KnowsSafeAtDef + <3>5. QED + BY <3>3, <3>4 + <2>. WITNESS 1cmsgs' \ 1cmsgs \in SUBSET {m \in msgsOfType("1c") : m.bal = b} + <2>. QED + BY <2>1 DEF msgs + +<1>5. ASSUME NEW self \in Ballot, Phase1a(self) + PROVE msgs' = msgs \cup {[type |-> "1a", bal |-> self]} + BY <1>5 DEF Phase1a, msgs, msgsOfType, 1bmsgs, 1bRestrict, 1cmsgs, KnowsSafeAt, + 2amsgs, acceptorMsgsOfType + +<1>6. ASSUME NEW self \in Ballot, Phase1c(self) + PROVE \E S \in SUBSET [type : {"1c"}, bal : {self}, val : Value]: + /\ \A m \in S : + \E a \in Acceptor : KnowsSafeAt(a, m.bal, m.val) + /\ msgs' = msgs \cup S + <2>1. PICK S \in SUBSET [type : {"1c"}, bal : {self}, val : Value] : + /\ bmsgs' = bmsgs \cup S + /\ knowsSent' = knowsSent + BY <1>6 DEF Phase1c + <2> DEFINE SS == {m \in S : \E a \in Acceptor : KnowsSafeAt(a, m.bal, m.val)} + <2> SUFFICES msgs' = msgs \cup SS + BY <2>1, Zenon + <2>2. /\ msgsOfType("1a")' = msgsOfType("1a") + /\ 1bmsgs' = 1bmsgs + /\ 1cmsgs' = 1cmsgs \cup SS + /\ 2amsgs' = 2amsgs + /\ acceptorMsgsOfType("2b")' = acceptorMsgsOfType("2b") + BY <2>1 DEF msgsOfType, 1bmsgs, 1bRestrict, 1cmsgs, KnowsSafeAt, 2amsgs, acceptorMsgsOfType + <2>3. QED + BY <2>2 DEF msgs + +<1>7. ASSUME NEW self \in FakeAcceptor, FakingAcceptor(self) + PROVE msgs' = msgs + BY <1>7, BQA DEF FakingAcceptor, msgs, 1bMessage, 2avMessage, 2bMessage, + msgsOfType, 1cmsgs, KnowsSafeAt, 1bmsgs, 2amsgs, acceptorMsgsOfType, msgsOfType + +<1>9. QED + BY <1>1, <1>2, <1>3, <1>4, <1>5, <1>6, <1>7, Zenon + +----------------------------------------------------------------------------- +(***************************************************************************) +(* We now come to the proof of invariance of our inductive invariant Inv. *) +(***************************************************************************) +THEOREM Invariance == Spec => []Inv +<1>1. Init => Inv + BY FS_EmptySet DEF Init, Inv, TypeOK, bmsgsFinite, 1bOr2bMsgs, 1bInv1, 1bInv2, + maxBalInv, 2avInv1, 2avInv2, 2avInv3, accInv, knowsSentInv + +<1>2. Inv /\ [Next]_vars => Inv' + <2> SUFFICES ASSUME Inv, [Next]_vars + PROVE Inv' + OBVIOUS + <2>1. ASSUME NEW self \in Acceptor, + NEW b \in Ballot, + \/ Phase1b(self, b) + \/ Phase2av(self, b) + \/ Phase2b(self,b) + \/ LearnsSent(self, b) + PROVE Inv' + <3>1. CASE Phase1b(self, b) + <4> DEFINE mb == [type |-> "1b", bal |-> b, acc |-> self, + m2av |-> 2avSent[self], + mbal |-> maxVBal[self], mval |-> maxVVal[self]] + mc == [type |-> "1b", acc |-> self, bal |-> b, + mbal |-> maxVBal[self], mval |-> maxVVal[self]] + <4>1. msgs' = msgs \cup {mc} + BY <3>1, MsgsLemma DEF Inv + <4>2. TypeOK' + BY <3>1 DEF Inv, TypeOK, BMessage, 1bMessage, ByzAcceptor, Phase1b + <4>3. bmsgsFinite' + BY <3>1, FiniteMsgsLemma, Zenon DEF Inv, bmsgsFinite, Phase1b + <4>4. 1bInv1' + BY <3>1, <4>1, IsaT(100) DEF Phase1b, 1bInv1, Inv, accInv + <4>5. 1bInv2' + BY <3>1 DEF Phase1b, 1bInv2, Inv, maxBalInv, TypeOK, 1bMessage, Ballot + <4>6. maxBalInv' + BY <3>1, BMessageLemma DEF Phase1b, maxBalInv, Ballot, Inv, TypeOK, + 1bMessage, 2avMessage, 2bMessage + <4>7. 2avInv1' + BY <3>1 DEF Phase1b, Inv, 2avInv1 + <4>8. 2avInv2' + BY <3>1 DEF Phase1b, Inv, 2avInv2 + <4>9. 2avInv3' + BY <3>1, <4>1 DEF Phase1b, Inv, 2avInv3 + <4>10. accInv' + <5> SUFFICES ASSUME NEW a \in Acceptor, + NEW r \in 2avSent[a] + PROVE /\ r.bal =< maxBal'[a] + /\ [type |-> "1c", bal |-> r.bal, val |-> r.val] + \in msgs' + BY <3>1, Zenon DEF accInv, Phase1b + <5> [type |-> "1c", bal |-> r.bal, val |-> r.val] \in msgs' + BY <3>1, MsgsLemma DEF Inv, accInv + <5> QED + BY <3>1 DEF Phase1b, Inv, Ballot, TypeOK, accInv + <4>11. knowsSentInv' + BY <3>1 DEF Phase1b, Inv, knowsSentInv, msgsOfType + <4>12. QED + BY <4>2, <4>3, <4>4, <4>5, <4>6, <4>7, <4>8, <4>9, <4>10, <4>11 DEF Inv + <3>2. CASE Phase2av(self, b) + <4>1. PICK mc \in sentMsgs("1c", b) : + /\ KnowsSafeAt(self, b, mc.val) + /\ bmsgs' = bmsgs \cup + {[type |-> "2av", bal |-> b, + val |-> mc.val, acc |-> self]} + /\ 2avSent' = [2avSent EXCEPT + ![self] = {r \in 2avSent[self] : r.val # mc.val} + \cup {[val |-> mc.val, bal |-> b]}] + BY <3>2, Zenon DEF Phase2av + <4>2. mc = [type |-> "1c", bal |-> mc.bal, val |-> mc.val] + BY <4>1, BMessageLemma DEF sentMsgs, Inv, TypeOK, 1cMessage + <4> DEFINE mb == [type |-> "2av", bal |-> b, + val |-> mc.val, acc |-> self] + mmc(v) == [type |-> "1c", bal |-> b, val |-> v] + ma(v) == [type |-> "2a", bal |-> b, val |-> v] + <4>3. \/ msgs' = msgs + \/ \E v \in Value : + /\ mmc(v) \in msgs + /\ msgs' = msgs \cup {ma(v)} + BY <3>2, MsgsLemma, Zenon DEF Inv + <4>4. msgs \subseteq msgs' + BY <4>3, Zenon + <4>5. TypeOK' + BY <3>2, <4>1, BMessageLemma + DEF sentMsgs, Inv, TypeOK, 1cMessage, Phase2av, 2avMessage, ByzAcceptor, BMessage + <4>6. bmsgsFinite' + BY <4>1, FiniteMsgsLemma, Zenon DEF Inv, bmsgsFinite + <4>7. 1bInv1' + BY <3>2, <4>1, <4>3, IsaT(100) DEF Phase2av, 1bInv1, Inv + <4>8. 1bInv2' + BY <4>1 DEF Inv, 1bInv2 + <4>9. maxBalInv' + BY <3>2, <4>1, BMessageLemma + DEF Phase2av, maxBalInv, Ballot, Inv, TypeOK, 1bMessage, 2avMessage, 2bMessage + <4>10. 2avInv1' + BY <3>2, <4>1 DEF Phase2av, Inv, 2avInv1, 2avInv2, TypeOK, 1bMessage, Ballot + <4>11. 2avInv2' + <5>1. SUFFICES ASSUME NEW m \in bmsgs', + 2avInv2!(m)!1 + PROVE \E r \in 2avSent'[m.acc] : /\ r.val = m.val + /\ r.bal >= m.bal + BY DEF 2avInv2 + <5>2. CASE m.acc = self + <6>1. CASE m = mb + BY <4>1, <6>1, Isa DEF Inv, TypeOK, Ballot + <6>2. CASE m # mb + <7>1. m \in bmsgs + BY <4>1, <6>2 + <7>2. PICK r \in 2avSent[m.acc] : /\ r.val = m.val + /\ r.bal >= m.bal + BY <5>1, <7>1 DEF Inv, 2avInv2 + <7>3. CASE r.val = mc.val + <8>. DEFINE rr == [val |-> mc.val, bal |-> b] + <8>. rr \in 2avSent'[m.acc] + BY <4>1, <5>2 DEF Inv, TypeOK + <8>. WITNESS rr \in 2avSent'[m.acc] + <8>. QED + BY <7>2, <7>3, <5>2, <5>1, <3>2, BMessageLemma + DEF Phase2av, Inv, TypeOK, accInv, Ballot, 2avMessage + <7>4. CASE r.val # mc.val + BY <7>2, <4>1, <5>2, <7>4 DEF Inv, TypeOK + <7>5. QED + BY <7>3, <7>4 + <6>3. QED + BY <6>1, <6>2 + <5>3. CASE m.acc # self + BY <5>3, <5>1, <4>1, BMessageLemma DEF Inv, TypeOK, 2avInv2, 2avMessage + <5>4. QED + BY <5>2, <5>3 + <4>12. 2avInv3' + BY <4>1, <4>2, <4>4 DEF Inv, 2avInv3, sentMsgs, msgs, 1cmsgs, msgsOfType + <4>13. accInv' + <5>1. SUFFICES ASSUME NEW a \in Acceptor, + NEW r \in 2avSent'[a] + PROVE /\ r.bal =< maxBal'[a] + /\ [type |-> "1c", bal |-> r.bal, val |-> r.val] + \in msgs' + BY Zenon DEF accInv + <5>2. CASE r \in 2avSent[a] + BY <5>2, <4>4, <4>5, <3>2 DEF Phase2av, Inv, TypeOK, accInv, Ballot + <5>3. CASE r \notin 2avSent[a] + BY <5>3, <3>2, <4>1, <4>2, <4>4 + DEF Phase2av, Inv, TypeOK, sentMsgs, msgsOfType, msgs, 1cmsgs, Ballot + <5>4. QED + BY <5>2, <5>3 + <4>14. knowsSentInv' + BY <3>2, <4>1 DEF Phase2av, Inv, knowsSentInv, msgsOfType + <4>15. QED + BY <4>5, <4>6, <4>7, <4>8, <4>9, <4>10, <4>11, <4>12, <4>13, <4>14 DEF Inv + <3>3. CASE Phase2b(self, b) + <4>1. PICK v \in Value : + /\ \E Q \in ByzQuorum : + \A a \in Q : + \E m \in sentMsgs("2av", b) : /\ m.val = v + /\ m.acc = a + /\ msgs' = msgs \cup + {[type |-> "2b", acc |-> self, bal |-> b, val |-> v]} + /\ bmsgs' = (bmsgs \cup + {[type |-> "2b", acc |-> self, bal |-> b, val |-> v]}) + /\ maxVVal' = [maxVVal EXCEPT ![self] = v] + BY <3>3, MsgsLemma DEF Inv + <4> DEFINE mb == [type |-> "2b", acc |-> self, bal |-> b, val |-> v] + <4>2. TypeOK' + BY <3>3, <4>1 DEF Phase2b, Inv, TypeOK, BMessage, 2bMessage, ByzAcceptor + <4>3. bmsgsFinite' + BY <4>1, FiniteMsgsLemma, Zenon DEF Inv, bmsgsFinite + <4>4. 1bInv1' + BY <4>1, Isa DEF Inv, 1bInv1 + <4>5. 1bInv2' + BY <4>1 DEF Inv, 1bInv2 + <4>6. maxBalInv' + BY <3>3, <4>1, <4>2, BMessageLemma + DEF Phase2b, Inv, maxBalInv, TypeOK, Ballot, 1bMessage, 2avMessage, 2bMessage + <4>7. 2avInv1' + BY <4>1 DEF Inv, 2avInv1 + <4>8. 2avInv2' + BY <3>3, <4>1 DEF Phase2b, Inv, TypeOK, 2avInv2 + <4>9. 2avInv3' + BY <4>1 DEF Inv, 2avInv3 + <4>10. accInv' + <5> SUFFICES ASSUME NEW a \in Acceptor, + NEW r \in 2avSent[a] + PROVE /\ r.bal =< maxBal'[a] + /\ [type |-> "1c", bal |-> r.bal, val |-> r.val] + \in msgs' + BY <3>3, Zenon DEF accInv, Phase2b + <5> [type |-> "1c", bal |-> r.bal, val |-> r.val] \in msgs' + BY <3>3, MsgsLemma DEF Inv, accInv + <5> QED + BY <3>3 DEF Phase2b, Inv, Ballot, TypeOK, accInv + <4>11. knowsSentInv' + BY <3>3, <4>1 DEF Phase2b, Inv, knowsSentInv, msgsOfType + <4>12. QED + BY <4>2, <4>3, <4>4, <4>5, <4>6, <4>7, <4>8, <4>9, <4>10, <4>11 DEF Inv + <3>4. CASE LearnsSent(self, b) + <4>1. PICK MS : /\ MS \subseteq {m \in msgsOfType("1c") : m.bal = b} + /\ msgs' = msgs \cup MS + BY <3>4, MsgsLemma, Zenon DEF Inv + <4>2. PICK S : + /\ S \subseteq sentMsgs("1b", b) + /\ knowsSent' = + [knowsSent EXCEPT ![self] = knowsSent[self] \cup S] + BY <3>4, Zenon DEF LearnsSent + <4>3. TypeOK' + BY <3>4, <4>2, BMessageLemma DEF Inv, TypeOK, sentMsgs, LearnsSent + <4>4. bmsgsFinite' + BY <3>4 DEF LearnsSent, Inv, bmsgsFinite, 1bOr2bMsgs + <4>5. 1bInv1' + BY <3>4, <4>1, Zenon DEF LearnsSent, Inv, 1bInv1 + <4>6. 1bInv2' + BY <3>4 DEF LearnsSent, Inv, 1bInv2 + <4>7. maxBalInv' + BY <3>4 DEF LearnsSent, Inv, maxBalInv + <4>8. 2avInv1' + BY <3>4 DEF LearnsSent, Inv, 2avInv1 + <4>9. 2avInv2' + BY <3>4 DEF LearnsSent, Inv, 2avInv2 + <4>10. 2avInv3' + BY <3>4, <4>1 DEF LearnsSent, Inv, 2avInv3 + <4>11. accInv' + BY <3>4, <4>1, Zenon DEF LearnsSent, Inv, accInv + <4>12. knowsSentInv' + BY <3>4, <4>2 DEF LearnsSent, Inv, TypeOK, knowsSentInv, sentMsgs, msgsOfType + <4>13. QED + BY <4>3, <4>4, <4>5, <4>6, <4>7, <4>8, <4>9, <4>10, <4>11, <4>12 DEF Inv + <3>5. QED + BY <2>1, <3>1, <3>2, <3>3, <3>4 + <2>2. ASSUME NEW self \in Ballot, + \/ Phase1a(self) + \/ Phase1c(self) + PROVE Inv' + <3>1. CASE Phase1a(self) + <4> DEFINE ma == [type |-> "1a", bal |-> self] + <4>1. msgs' = msgs \cup {ma} + BY <3>1, MsgsLemma DEF Inv + <4>2. TypeOK' + BY <3>1 DEF Phase1a, Inv, TypeOK, BMessage, 1aMessage + <4>3. bmsgsFinite' + BY <3>1, FiniteMsgsLemma, Zenon DEF Inv, bmsgsFinite, Phase1a + <4>4. 1bInv1' + BY <3>1, <4>1, Isa DEF Phase1a, Inv, 1bInv1 + <4>5. 1bInv2' + BY <3>1 DEF Phase1a, Inv, 1bInv2 + <4>6. maxBalInv' + BY <3>1 DEF Phase1a, Inv, maxBalInv + <4>7. 2avInv1' + BY <3>1 DEF Phase1a, Inv, 2avInv1 + <4>8. 2avInv2' + BY <3>1 DEF Phase1a, Inv, 2avInv2 + <4>9. 2avInv3' + BY <3>1, <4>1 DEF Phase1a, Inv, 2avInv3 + <4>10. accInv' + BY <3>1, <4>1, Zenon DEF Phase1a, Inv, accInv + <4>11. knowsSentInv' + BY <3>1 DEF Inv, knowsSentInv, msgsOfType, Phase1a + <4>12. QED + BY <4>2, <4>3, <4>4, <4>5, <4>6, <4>7, <4>8, <4>9, <4>10, <4>11 DEF Inv + <3>2. CASE Phase1c(self) + <4>1. PICK S : /\ S \in SUBSET [type : {"1c"}, bal : {self}, val : Value] + /\ bmsgs' = bmsgs \cup S + BY <3>2 DEF Phase1c + <4>2. PICK MS : + /\ MS \in SUBSET [type : {"1c"}, bal : {self}, val : Value] + /\ \A m \in MS : + \E a \in Acceptor : KnowsSafeAt(a, m.bal, m.val) + /\ msgs' = msgs \cup MS + BY <3>2, MsgsLemma DEF Inv + <4>3. TypeOK' + BY <3>2, <4>1 DEF Phase1c, Inv, TypeOK, BMessage, 1cMessage + <4>4. bmsgsFinite' + BY <4>1 DEF Inv, bmsgsFinite, 1bOr2bMsgs + <4>5. 1bInv1' + BY <3>2, <4>2, Zenon DEF Phase1c, Inv, 1bInv1 + <4>6. 1bInv2' + BY <4>1 DEF Inv, 1bInv2 + <4>7. maxBalInv' + BY <3>2 DEF Phase1c, Inv, maxBalInv + <4>8. 2avInv1' + BY <4>1 DEF Inv, 2avInv1 + <4>9. 2avInv2' + BY <3>2 DEF Phase1c, Inv, 2avInv2 + <4>10. 2avInv3' + BY <3>2, <4>2 DEF Phase1c, Inv, 2avInv3 + <4>11. accInv' + BY <3>2, <4>2, Zenon DEF Phase1c, Inv, accInv + <4>12. knowsSentInv' + BY <3>2 DEF Inv, knowsSentInv, msgsOfType, Phase1c + <4>13. QED + BY <4>3, <4>4, <4>5, <4>6, <4>7, <4>8, <4>9, <4>10, <4>11, <4>12 DEF Inv + <3>3. QED + BY <3>1, <3>2, <2>2 + <2>3. ASSUME NEW self \in FakeAcceptor, + FakingAcceptor(self) + PROVE Inv' + <3>1. PICK m \in 1bMessage \cup 2avMessage \cup 2bMessage : + /\ m.acc \notin Acceptor + /\ bmsgs' = bmsgs \cup {m} + BY <2>3, BQA DEF FakingAcceptor + <3>2. msgs' = msgs + BY <2>3, MsgsLemma DEF Inv + <3>3. TypeOK' + BY <2>3, <3>1 DEF Inv, TypeOK, BMessage, FakingAcceptor + <3>4. bmsgsFinite' + BY <3>1, FiniteMsgsLemma DEF Inv, TypeOK + <3>5. 1bInv1' + BY <3>1, <3>2, Zenon DEF Inv, 1bInv1 + <3>6. 1bInv2' + BY <3>1 DEF Inv, 1bInv2 + <3>7. maxBalInv' + BY <2>3, <3>1 DEF Inv, maxBalInv, FakingAcceptor + <3>8. 2avInv1' + BY <3>1 DEF Inv, 2avInv1 + <3>9. 2avInv2' + BY <2>3, <3>1 DEF Inv, 2avInv2, FakingAcceptor + <3>10. 2avInv3' + BY <3>1, <3>2 DEF Inv, 2avInv3 + <3>11. accInv' + BY <2>3, <3>2, Zenon DEF Inv, accInv, FakingAcceptor + <3>12. knowsSentInv' + BY <2>3, <3>1 DEF Inv, knowsSentInv, msgsOfType, FakingAcceptor + <3>13. QED + BY <3>3, <3>4, <3>5, <3>6, <3>7, <3>8, <3>9, <3>10, <3>11, <3>12 DEF Inv + <2>4. ASSUME UNCHANGED vars + PROVE Inv' + <3> USE UNCHANGED vars DEF Inv, vars + <3> msgs = msgs' + BY DEF msgs, msgsOfType, 1bmsgs, 1bRestrict, acceptorMsgsOfType, 1cmsgs, + KnowsSafeAt, 2amsgs + <3> QED + BY DEF TypeOK, bmsgsFinite, 1bOr2bMsgs, 1bInv1, 1bInv2, + maxBalInv, 2avInv1, 2avInv2, 2avInv3, accInv, knowsSentInv, msgsOfType + <2>5. QED + BY <2>1, <2>2, <2>3, <2>4, NextDef + +<1>3. QED + BY <1>1, <1>2, PTL DEF Spec + +----------------------------------------------------------------------------- +(***************************************************************************) +(* We next use the invariance of Inv to prove that algorithm BPCon *) +(* implements algorithm PCon under the refinement mapping *) +(* defined by the INSTANCE statement above. *) +(***************************************************************************) +AbstractSpec == P!Spec +THEOREM Spec => P!Spec +<1>1. Init => P!Init + <2>. HAVE Init + <2>1. MaxBallot({}) = -1 + BY MaxBallotProp, FS_EmptySet + <2>2. P!Init!1 /\ P!Init!2 /\ P!Init!3 + BY <2>1 DEF Init, PmaxBal, 1bOr2bMsgs, None, P!None + <2>3. msgs = {} + BY BQA DEF Init, msgsOfType, acceptorMsgsOfType, 1bmsgs, 1cmsgs, 2amsgs, Quorum, msgs + <2>4. QED + BY <2>2, <2>3 DEF P!Init + +<1>2. Inv /\ Inv' /\ [Next]_vars => [P!Next]_P!vars + <2> InvP == Inv' + <2> SUFFICES ASSUME Inv, InvP, Next + PROVE P!TLANext \/ P!vars' = P!vars + <3> UNCHANGED vars => UNCHANGED P!vars + BY DEF vars, P!vars, PmaxBal, 1bOr2bMsgs, msgs, msgsOfType, acceptorMsgsOfType, + 1bmsgs, 2amsgs, 1cmsgs, KnowsSafeAt + <3> QED + BY PNextDef DEF Inv, P!ProcSet, P!Init, Ballot, P!Ballot + <2> HIDE DEF InvP + <2>2. \A a \in Acceptor : PmaxBal[a] \in Ballot \cup {-1} + BY PMaxBalLemma3, MaxBallotProp DEF Inv, PmaxBal, 1bOr2bMsgs + <2>3. ASSUME NEW self \in Acceptor, NEW b \in Ballot, + Phase1b(self, b) + PROVE P!TLANext \/ P!vars' = P!vars + <3>1. msgs' = msgs \cup {[type |-> "1b", acc |-> self, bal |-> b, + mbal |-> maxVBal[self], mval |-> maxVVal[self]]} + BY <2>3, MsgsLemma DEF Inv + <3>2. P!sentMsgs("1a", b) # {} + BY <2>3 DEF Phase1b, sentMsgs, msgsOfType, msgs, P!sentMsgs + <3>3. UNCHANGED <> + BY <2>3 DEF Phase1b + <3>4. b > PmaxBal[self] + BY <2>2, <2>3, PmaxBalLemma4 DEF Phase1b, Inv, TypeOK, Ballot + <3>5. PmaxBal' = [PmaxBal EXCEPT ![self] = b] + <4> DEFINE m == [type |-> "1b", bal |-> b, acc |-> self, + m2av |-> 2avSent[self], + mbal |-> maxVBal[self], mval |-> maxVVal[self]] + mA(a) == {ma \in bmsgs : /\ ma.type \in {"1b", "2b"} + /\ ma.acc = a} + S(a) == {ma.bal : ma \in mA(a)} + <4>1. bmsgs' = bmsgs \cup {m} + BY <2>3 DEF Phase1b + <4>2. mA(self)' = mA(self) \cup {m} + BY <4>1 + <4>3. /\ PmaxBal = [a \in Acceptor |-> MaxBallot(S(a))] + /\ PmaxBal' = [a \in Acceptor |-> MaxBallot(S(a))'] + BY DEF PmaxBal, 1bOr2bMsgs + <4> HIDE DEF mA + <4>4. S(self)' = S(self) \cup {b} + BY <4>2, Isa + <4>5. MaxBallot(S(self) \cup {b}) = b + <5> DEFINE SS == S(self) \cup {b} + <5>1. IsFiniteSet(S(self)) + <6>. IsFiniteSet(mA(self)) + BY FS_Subset DEF Inv, bmsgsFinite, mA, 1bOr2bMsgs + <6>. QED + BY FS_Image, Isa + <5>2. IsFiniteSet(SS) + BY <5>1, FS_AddElement + <5>3. S(self) \subseteq Ballot \cup {-1} + BY BMessageLemma DEF mA, Inv, TypeOK, 1bMessage, 2bMessage + <5>4. \A x \in SS : b >= x + BY <3>4, <4>3, <5>1, <5>3, MaxBallotProp, Z3T(10) DEF Ballot + <5>5. QED + BY <5>2, <5>3, <5>4, MaxBallotLemma1 + <4>6. \A a \in Acceptor : a # self => S(a)' = S(a) + BY <4>1 DEF mA + <4>7. QED + BY <4>3, <4>4, <4>5, <4>6, Zenon DEF PmaxBal, 1bOr2bMsgs + <3>6. QED + BY <3>1, <3>2, <3>3, <3>4, <3>5, Zenon DEF P!TLANext, P!Ballot, Ballot, P!Phase1b + <2>4. ASSUME NEW self \in Acceptor, NEW b \in Ballot, + Phase2av(self, b) + PROVE P!TLANext \/ P!vars' = P!vars + <3>1. PmaxBal' = PmaxBal + <4> DEFINE mm(m) == [type |-> "2av", bal |-> b, + val |-> m.val, acc |-> self] + <4>1. PICK m : bmsgs' = bmsgs \cup {mm(m)} + BY <2>4 DEF Phase2av + <4>2. mm(m).type = "2av" + OBVIOUS + <4> QED + BY <4>1, <4>2, PmaxBalLemma1, Zenon + <3>2. CASE msgs' = msgs + BY <3>1, <3>2, <2>4 DEF Phase2av, P!vars + <3>3. CASE /\ msgs' # msgs + /\ \E v \in Value : + /\ [type |-> "1c", bal |-> b, val |-> v] \in msgs + /\ msgs' = msgs \cup {[type |-> "2a", bal |-> b, val |-> v]} + <4>1. PICK v \in Value : + /\ [type |-> "1c", bal |-> b, val |-> v] \in msgs + /\ msgs' = msgs \cup {[type |-> "2a", bal |-> b, val |-> v]} + BY <3>3 + <4>2. P!sentMsgs("2a", b) = {} + <5>1. SUFFICES ASSUME NEW m \in P!sentMsgs("2a", b) + PROVE m = [type |-> "2a", bal |-> b, val |-> v] + BY <3>3, <4>1 DEF P!sentMsgs + <5>2. /\ m \in 2amsgs + /\ m.type = "2a" + /\ m.bal = b + BY MsgsTypeLemma DEF P!sentMsgs + <5>3. PICK Q \in Quorum : + \A a \in Q : + \E mav \in acceptorMsgsOfType("2av") : + /\ mav.acc = a + /\ mav.bal = b + /\ mav.val = m.val + BY <5>2 DEF 2amsgs + <5>4. PICK Q2 \in Quorum : + \A a \in Q2 : + \E m2av \in acceptorMsgsOfType("2av")' : + /\ m2av.acc = a + /\ m2av.bal = b + /\ m2av.val = v + BY <4>1, MsgsTypeLemmaPrime, Isa DEF 2amsgs + <5>5. PICK a \in Q \cap Q2 : a \in Acceptor + BY QuorumTheorem + <5>6. PICK mav \in acceptorMsgsOfType("2av") : + /\ mav.acc = a + /\ mav.bal = b + /\ mav.val = m.val + BY <5>3, <5>5 + <5>7. PICK m2av \in acceptorMsgsOfType("2av")' : + /\ m2av.acc = a + /\ m2av.bal = b + /\ m2av.val = v + BY <5>4, <5>5 + <5>8. mav \in acceptorMsgsOfType("2av")' + BY <2>4 DEF acceptorMsgsOfType, msgsOfType, Phase2av + <5>9. m.val = v + BY <5>5, <5>6, <5>7, <5>8 DEF 2avInv1, InvP, Inv, acceptorMsgsOfType, msgsOfType + <5>10. QED + BY <5>2, <5>9 DEF 2amsgs + <4>4. QED + BY <2>4, <3>1, <4>1, <4>2 DEF P!TLANext, P!Phase2a, Phase2av, Ballot, P!Ballot + <3>4.\/ msgs' = msgs + \/ (/\ msgs' # msgs + /\ \E v \in Value : + /\ [type |-> "1c", bal |-> b, val |-> v] \in msgs + /\ msgs' = msgs \cup {[type |-> "2a", bal |-> b, val |-> v]}) + BY MsgsLemma, <2>4, Zenon DEF Inv + <3>5. QED + BY <3>2, <3>3, <3>4 + <2>5. ASSUME NEW self \in Acceptor, NEW b \in Ballot, + Phase2b(self, b) + PROVE P!TLANext \/ P!vars' = P!vars + <3>1. PmaxBal[self] =< b + <4>1. PmaxBal[self] =< maxBal[self] + BY PmaxBalLemma4 DEF Inv + <4>2. maxBal[self] =< b + BY <2>5 DEF Phase2b + <4>3. QED + BY <4>1, <4>2, PmaxBalLemma5 DEF Inv, TypeOK, Ballot + <3>2. PICK v \in Value : + /\ \E Q \in ByzQuorum : + \A a \in Q : + \E m \in sentMsgs("2av", b) : /\ m.val = v + /\ m.acc = a + /\ msgs' = msgs \cup + {[type |-> "2b", acc |-> self, bal |-> b, val |-> v]} + /\ bmsgs' = bmsgs \cup + {[type |-> "2b", acc |-> self, bal |-> b, val |-> v]} + /\ maxVVal' = [maxVVal EXCEPT ![self] = v] + BY <2>5, MsgsLemma DEF Inv + <3> DEFINE m == [type |-> "2a", bal |-> b, val |-> v] + m2b == [type |-> "2b", acc |-> self, bal |-> b, val |-> v] + <3>3. m \in P!sentMsgs("2a", b) + <4>1. PICK Q \in Quorum : + \A a \in Q : + \E mm \in sentMsgs("2av", b) : /\ mm.val = v + /\ mm.acc = a + BY <3>2, Isa DEF Quorum + <4>2. m \in 2amsgs + BY <4>1 DEF sentMsgs, Quorum, acceptorMsgsOfType, msgsOfType, 2amsgs + <4>3. QED + BY <4>2 DEF P!sentMsgs, msgs + <3>4. PmaxBal' = [PmaxBal EXCEPT ![self] = b] + <4>1. ASSUME NEW a \in Acceptor, + a # self + PROVE PmaxBal'[a] = PmaxBal[a] + BY <3>2, <4>1, PmaxBalLemma2, m2b.acc = self, Zenon + <4>2. PmaxBal'[self] = b + <5> DEFINE S == {mm.bal : mm \in {ma \in bmsgs : + /\ ma.type \in {"1b", "2b"} + /\ ma.acc = self}} + T == S \cup {m2b.bal} + <5>1. IsFiniteSet(S) /\ (S \in SUBSET Ballot) + BY PMaxBalLemma3 DEF Inv + <5>2. IsFiniteSet(T) /\ (T \in SUBSET Ballot) + BY <5>1, FS_AddElement + <5>3. PmaxBal[self] = MaxBallot(S) + BY DEF PmaxBal, 1bOr2bMsgs + <5>4. PmaxBal'[self] = MaxBallot(T) + BY <3>2, Zenon DEF PmaxBal, 1bOr2bMsgs + <5> HIDE DEF S + <5>5. CASE S = {} + <6> MaxBallot({b}) = b + BY FS_Singleton, MaxBallotLemma1, Isa DEF Ballot + <6> QED + BY <5>4, <5>5 + <5>6. CASE S # {} + <6> \A bb \in T : b >= bb + BY <3>1, <5>1, <5>3, MaxBallotProp, PmaxBalLemma5 DEF Inv, Ballot + <6> QED + BY <5>2, <5>4, MaxBallotLemma1 + <5>7. QED + BY <5>5, <5>6 + <4>3. QED + BY <4>1, <4>2, Zenon DEF PmaxBal, 1bOr2bMsgs + <3>5. /\ maxVBal' = [maxVBal EXCEPT ![self] = b] + /\ maxVVal' = [maxVVal EXCEPT ![self] = m.val] + BY <2>5, <3>2, Zenon DEF Phase2b + <3>6. QED + BY <3>1, <3>2, <3>3, <3>4, <3>5, Zenon + DEF P!TLANext, P!Phase2b, Ballot, P!Ballot + <2>6. ASSUME NEW self \in Acceptor, NEW b \in Ballot, + LearnsSent(self, b) + PROVE P!TLANext \/ P!vars' = P!vars + <3>1. PICK SM \in SUBSET {m \in msgsOfType("1c") : m.bal = b} : + msgs' = msgs \cup SM + BY <2>6, MsgsLemma DEF Inv + <3> DEFINE S == {m.val : m \in SM} + <3>2. S \in SUBSET Value + BY BMessageLemma DEF Inv, TypeOK, msgsOfType, 1cMessage + <3>3. msgs' = msgs \cup {[type |-> "1c", bal |-> b, val |-> v] : v \in S} + BY <3>1, BMessageLemma DEF Inv, TypeOK, msgsOfType, 1cMessage + <3>4. ASSUME NEW v \in S + PROVE \E Q \in Quorum : P!ShowsSafeAt(Q, b, v) + <4>1. PICK ac \in Acceptor : KnowsSafeAt(ac, b, v)' + BY <3>1, MsgsTypeLemmaPrime DEF msgsOfType, 1cmsgs + <4>2. bmsgs' = bmsgs + BY <2>6 DEF LearnsSent + <4> DEFINE Q(BQ) == BQ \cap Acceptor + SS == {m \in knowsSent'[ac] : m.bal = b} + SQ(BQ) == {1bRestrict(mm) : + mm \in {m \in SS : m.acc \in Q(BQ)}} + Q1b(BQ) == {m \in P!sentMsgs("1b", b) : m.acc \in Q(BQ)} + <4>3. ASSUME NEW BQ \in ByzQuorum, + \A a \in BQ : \E m \in SS : m.acc = a + PROVE SQ(BQ) = Q1b(BQ) + <5>1. ASSUME NEW m \in P!sentMsgs("1b", b), + m.acc \in Q(BQ) + PROVE m \in SQ(BQ) + BY <4>2, <4>3, <5>1, MsgsTypeLemma + DEF P!sentMsgs, msgs, 1bmsgs, acceptorMsgsOfType, msgsOfType, + 1bRestrict, InvP, Inv, knowsSentInv, 1bInv2 + <5>2. ASSUME NEW m \in SS, + m.acc \in Q(BQ) + PROVE 1bRestrict(m) \in Q1b(BQ) + BY <4>2, <5>2 + DEF InvP, Inv, knowsSentInv, msgsOfType, acceptorMsgsOfType, msgs, + 1bmsgs, P!sentMsgs, 1bRestrict + <5>3. QED + BY <5>1, <5>2 DEF Q1b, SQ + <4>4. CASE KnowsSafeAt(ac, b, v)!1!1' + <5>1. PICK BQ \in ByzQuorum : KnowsSafeAt(ac, b, v)!1!1!(BQ)' + BY <4>4 + <5>2. \A a \in Q(BQ) : \E m \in SQ(BQ) : /\ m.acc = a + /\ m.mbal = -1 + BY <5>1, Isa DEF 1bRestrict + <5>3. \A m \in SQ(BQ) : m.mbal = -1 + BY <4>2, <5>2 + DEF InvP, Inv, knowsSentInv, msgsOfType, 1bRestrict, 1bInv2 + <5>4. SQ(BQ) = Q1b(BQ) + BY <4>3, <5>1 + <5>5. Q(BQ) \in Quorum + BY DEF Quorum + <5> HIDE DEF SS, Q, SQ + <5> WITNESS Q(BQ) \in Quorum + <5>6. QED + BY <5>2, <5>3, <5>4 DEF P!ShowsSafeAt + <4>5. CASE KnowsSafeAt(ac, b, v)!1!2' + <5>1. PICK c \in 0..(b-1) : KnowsSafeAt(ac, b, v)!1!2!(c)' + BY <4>5 + <5>2. PICK BQ \in ByzQuorum : + \A a \in BQ : \E m \in SS : /\ m.acc = a + /\ m.mbal =< c + /\ (m.mbal = c) => (m.mval = v) + BY <5>1 + <5>3. SQ(BQ) = Q1b(BQ) + BY <5>2, <4>3 + <5>4. P!ShowsSafeAt(Q(BQ), b, v)!1!1 + <6>1. SUFFICES ASSUME NEW a \in Q(BQ) + PROVE \E m \in Q1b(BQ) : m.acc = a + OBVIOUS + <6>2. PICK m \in SS : m.acc = a + BY <5>2 + <6>3. /\ 1bRestrict(m) \in SQ(BQ) + /\ 1bRestrict(m).acc = a + BY <6>2 DEF 1bRestrict + <6>. QED + BY <6>3, <5>3 + <5>5. PICK m1c \in msgs : + /\ m1c = [type |-> "1c", bal |-> m1c.bal, val |-> v] + /\ m1c.bal >= c + /\ m1c.bal \in Ballot + <6>1. PICK WQ \in WeakQuorum : + \A a \in WQ : \E m \in SS : /\ m.acc = a + /\ \E r \in m.m2av : + /\ r.bal >= c + /\ r.val = v + BY <5>1 + <6>2. PICK a \in WQ, m \in SS : + /\ a \in Acceptor + /\ m.acc = a + /\ \E r \in m.m2av : /\ r.bal >= c + /\ r.val = v + BY <6>1, BQA + <6>4. PICK r \in m.m2av : /\ r.bal >= c + /\ r.val = v + BY <6>2 + <6>5. /\ m.bal = b + /\ m \in bmsgs + /\ m.type = "1b" + /\ r.bal \in Ballot + BY <4>2, <6>2, BMessageLemma + DEF Inv, InvP, TypeOK, 1bMessage, knowsSentInv, msgsOfType + <6>. QED + BY <6>2, <6>4, <6>5, Zenon DEF Inv, 1bInv1 + <5>6. ASSUME NEW m \in Q1b(BQ) + PROVE /\ m1c.bal \geq m.mbal + /\ (m1c.bal = m.mbal) => (m.mval = v) + <6>1. PICK mm \in SS : /\ mm.acc = m.acc + /\ mm.mbal =< c + /\ (mm.mbal = c) => (mm.mval = v) + BY <5>2 + <6>2. PICK mm2 \in SS : /\ mm2.acc = m.acc + /\ m = 1bRestrict(mm2) + BY <5>3 DEF 1bRestrict + <6>3. /\ mm = mm2 + /\ mm2.mbal \in Ballot \cup {-1} + BY <4>2, <6>1, <6>2, BMessageLemma + DEF Inv, InvP, TypeOK, knowsSentInv, 1bInv2, msgsOfType, 1bMessage + <6>. QED + <7> \A m1cbal, mmbal \in Ballot \cup {-1} : + mmbal =< c /\ m1cbal >= c => /\ m1cbal \geq mmbal + /\ mmbal = m1cbal => mmbal = c + BY DEF Ballot + <7> QED + BY <5>5, <6>1, <6>2, <6>3 DEF 1bRestrict + <5>7. P!ShowsSafeAt(Q(BQ), b, v)!1!2!2!(m1c) + BY <5>5, <5>6 + <5>. QED + BY <5>4, <5>7, Isa DEF P!ShowsSafeAt, Quorum + <4>6. QED + BY <3>1, <4>1, <4>4, <4>5 DEF KnowsSafeAt + <3>6. QED + BY <2>6, <3>1, <3>2, <3>3, <3>4, Zenon + DEF LearnsSent, P!Phase1c, P!TLANext, Ballot, P!Ballot, PmaxBal, 1bOr2bMsgs + <2>7. ASSUME NEW self \in Ballot, + Phase1a(self) + PROVE P!TLANext \/ P!vars' = P!vars + <3>1. msgs' = msgs \cup {[type |-> "1a", bal |-> self]} + BY <2>7, MsgsLemma DEF Inv + <3>2. UNCHANGED << PmaxBal, maxVBal, maxVVal >> + BY <2>7, Isa DEF Phase1a, PmaxBal, 1bOr2bMsgs + <3>. QED + BY <3>1, <3>2 DEF P!Phase1a, P!TLANext, Ballot, P!Ballot + <2>8. ASSUME NEW self \in Ballot, + Phase1c(self) + PROVE P!TLANext \/ P!vars' = P!vars + <3>1. PICK SS \in SUBSET [type : {"1c"}, bal : {self}, val : Value] : + /\ \A m \in SS : \E a \in Acceptor : KnowsSafeAt(a, m.bal, m.val) + /\ msgs' = msgs \cup SS + BY <2>8, MsgsLemma DEF Inv + <3> DEFINE S == {m.val : m \in SS} + <3>2. SS = {[type |-> "1c", bal |-> self, val |-> v] : v \in S} + OBVIOUS + <3>3. ASSUME NEW v \in S + PROVE \E Q \in Quorum : P!ShowsSafeAt(Q, self, v) + <4> DEFINE m == [type |-> "1c", bal |-> self, val |-> v] + <4>1. PICK a \in Acceptor : KnowsSafeAt(a, self, v) + BY <3>1 + <4> DEFINE SK == {mm \in knowsSent[a] : mm.bal = self} + <4>2. ASSUME NEW BQ \in ByzQuorum, + \A ac \in BQ : \E mm \in SK : mm.acc = ac + PROVE P!ShowsSafeAt(BQ \cap Acceptor, self, v)!1!1 + <5> DEFINE Q == BQ \cap Acceptor + Q1b == {mm \in P!sentMsgs("1b", self) : mm.acc \in Q} + <5> SUFFICES ASSUME NEW ac \in BQ \cap Acceptor + PROVE \E mm \in Q1b : mm.acc = ac + OBVIOUS + <5>1. PICK mm \in SK : mm.acc = ac + BY <4>2 + <5>2. /\ 1bRestrict(mm) \in P!sentMsgs("1b", self) + /\ 1bRestrict(mm).acc = ac + BY <5>1 DEF acceptorMsgsOfType, msgsOfType, 1bmsgs, msgs, Inv, knowsSentInv, + 1bRestrict, P!sentMsgs + <5>. QED + BY <5>2 + <4>3. CASE KnowsSafeAt(a, self, v)!1!1 + <5>1. PICK BQ \in ByzQuorum : + \A ac \in BQ : \E mm \in SK : /\ mm.acc = ac + /\ mm.mbal = -1 + BY <4>3 + <5> DEFINE Q == BQ \cap Acceptor + Q1b == {mm \in P!sentMsgs("1b", self) : mm.acc \in Q} + <5>2. P!ShowsSafeAt(Q, self, v)!1!1 + BY <5>1, <4>2 + <5>3. ASSUME NEW mm \in Q1b + PROVE mm.mbal = -1 + BY <5>1, MsgsTypeLemma + DEF P!sentMsgs, 1bmsgs, acceptorMsgsOfType, msgsOfType, 1bRestrict, + Inv, knowsSentInv, 1bInv2, 1bRestrict + <5>. QED + BY <5>2, <5>3, Zenon DEF P!ShowsSafeAt, Quorum + <4>4. CASE KnowsSafeAt(a, self, v)!1!2 + <5>1. PICK c \in 0..(self-1) : KnowsSafeAt(a, self, v)!1!2!(c) + BY <4>4 + <5>2. PICK BQ \in ByzQuorum : KnowsSafeAt(a, self, v)!1!2!(c)!1!(BQ) + BY <5>1 + <5> DEFINE Q == BQ \cap Acceptor + Q1b == {mm \in P!sentMsgs("1b", self) : mm.acc \in Q} + <5>3. P!ShowsSafeAt(Q, self, v)!1!1 + BY <5>2, <4>2 + <5>4. PICK WQ \in WeakQuorum : KnowsSafeAt(a, self, v)!1!2!(c)!2!(WQ) + BY <5>1 + <5>5. PICK ac \in WQ \cap Acceptor : + KnowsSafeAt(a, self, v)!1!2!(c)!2!(WQ)!(ac) + BY <5>4, BQA + <5>6. PICK mk \in SK : /\ mk.acc = ac + /\ \E r \in mk.m2av : /\ r.bal >= c + /\ r.val = v + BY <5>5 + <5>7. PICK r \in mk.m2av : /\ r.bal >= c + /\ r.val = v + BY <5>6 + <5> DEFINE mc == [type |-> "1c", bal |-> r.bal, val |-> v] + <5>9. mc \in msgs + BY <5>6, <5>7 DEF Inv, 1bInv1, knowsSentInv, msgsOfType + <5>10. ASSUME NEW mq \in Q1b + PROVE /\ mc.bal \geq mq.mbal + /\ (mc.bal = mq.mbal) => (mq.mval = v) + BY <5>2, <5>7, MsgsTypeLemma, BMessageLemma + DEF P!sentMsgs, 1bmsgs, acceptorMsgsOfType, msgsOfType, 1bRestrict, + Inv, TypeOK, 1bInv2, knowsSentInv, 1bMessage, Ballot + <5>11. QED + <6> Q \in Quorum + BY DEF Quorum + <6> WITNESS Q \in Quorum + <6> QED + BY <5>3, <5>9, <5>10 DEF P!ShowsSafeAt + <4>5. QED + BY <4>1, <4>3, <4>4 DEF KnowsSafeAt + <3>. QED + BY <2>8, <3>1, <3>2, <3>3, Zenon + DEF P!Phase1c, Phase1c, PmaxBal, 1bOr2bMsgs, P!TLANext, Ballot, P!Ballot + <2>9. ASSUME NEW self \in FakeAcceptor, + FakingAcceptor(self) + PROVE P!TLANext \/ P!vars' = P!vars + <3>1. msgs' = msgs + BY <2>9, MsgsLemma DEF Inv + <3>2. PmaxBal' = PmaxBal + BY <2>9, BQA, Zenon DEF FakingAcceptor, PmaxBal, 1bOr2bMsgs + <3>. QED + BY <2>9, <3>1, <3>2 DEF P!vars, FakingAcceptor + <2>10. QED + BY <2>3, <2>4, <2>5, <2>6, <2>7, <2>8, <2>9, NextDef + +<1>3. QED + BY <1>1, <1>2, Invariance, PTL DEF Spec, P!Spec + +----------------------------------------------------------------------------- +(***************************************************************************) +(* To see how learning is implemented, we must describe how to determine *) +(* that a value has been chosen. This is done by the following definition *) +(* of `chosen' to be the set of chosen values. *) +(***************************************************************************) +chosen == {v \in Value : \E BQ \in ByzQuorum, b \in Ballot : + \A a \in BQ : \E m \in msgs : /\ m.type = "2b" + /\ m.acc = a + /\ m.bal = b + /\ m.val = v} +(***************************************************************************) +(* The correctness of our definition of `chosen' is expressed by the *) +(* following theorem, which asserts that if a value is in `chosen', then *) +(* it is also in the set `chosen' of the emulated execution of the *) +(* PCon algorithm. *) +(* *) +(* The state function `chosen' does not necessarily equal the *) +(* corresponding state function of the PCon algorithm. It *) +(* requires every (real or fake) acceptor in a ByzQuorum to vote for (send *) +(* 2b messages) for a value v in the same ballot for v to be in `chosen' *) +(* for the BPCon algorithm, but it requires only that every (real) *) +(* acceptor in a Quorum vote for v in the same ballot for v to be in the *) +(* set `chosen' of the emulated execution of algorithm PCon. *) +(* *) +(* Liveness for BPCon requires that, under suitable assumptions, some *) +(* value is eventually in `chosen'. Since we can't assume that a fake *) +(* acceptor does anything useful, liveness requires the assumption that *) +(* there is a ByzQuorum composed entirely of real acceptors (the first *) +(* conjunct of assumption BQLA). *) +(***************************************************************************) +THEOREM chosen \subseteq P!chosen +BY Isa DEF chosen, P!chosen, Quorum, Ballot, P!Ballot + +============================================================================== +\* Modification History +\* Last modified Sat Jul 25 17:34:50 PDT 2020 by lamport +\* Last modified Fri Jul 24 17:51:34 CEST 2020 by merz +\* Last modified Wed Apr 15 15:16:26 CEST 2020 by doligez +\* Last modified Mon Aug 18 14:57:27 CEST 2014 by tomer +\* Last modified Mon Mar 04 17:24:05 CET 2013 by doligez +\* Last modified Wed Dec 01 11:35:29 PST 2010 by lamport diff --git a/specifications/byzpaxos/Consensus.cfg b/specifications/byzpaxos/Consensus.cfg new file mode 100644 index 00000000..9adf3c1a --- /dev/null +++ b/specifications/byzpaxos/Consensus.cfg @@ -0,0 +1,6 @@ +CONSTANT Value = {v1, v2, v3} +INVARIANTS TypeOK Inv +PROPERTY Success +SPECIFICATION LiveSpec +CHECK_DEADLOCK FALSE + diff --git a/specifications/byzpaxos/Consensus.tla b/specifications/byzpaxos/Consensus.tla new file mode 100644 index 00000000..769c33b1 --- /dev/null +++ b/specifications/byzpaxos/Consensus.tla @@ -0,0 +1,278 @@ +----------------------------- MODULE Consensus ------------------------------ +(***************************************************************************) +(* The consensus problem requires a set of processes to choose a single *) +(* value. This module specifies the problem by specifying exactly what *) +(* the requirements are for choosing a value. *) +(***************************************************************************) +EXTENDS Naturals, FiniteSets, FiniteSetTheorems, TLAPS + +(***************************************************************************) +(* We let the constant parameter Value be the set of all values that can *) +(* be chosen. *) +(***************************************************************************) +CONSTANT Value + +(**************************************************************************** +We now specify the safety property of consensus as a trivial algorithm +that describes the allowed behaviors of a consensus algorithm. It uses +the variable `chosen' to represent the set of all chosen values. The +algorithm is trivial; it allows only behaviors that contain a single +state-change in which the variable `chosen' is changed from its initial +value {} to the value {v} for an arbitrary value v in Value. The +algorithm itself does not specify any fairness properties, so it also +allows a behavior in which `chosen' is not changed. We could use a +translator option to have the translation include a fairness +requirement, but we don't bother because it is easy enough to add it by +hand to the safety specification that the translator produces. + +A real specification of consensus would also include additional +variables and actions. In particular, it would have Propose actions in +which clients propose values and Learn actions in which clients learn +what value has been chosen. It would allow only a proposed value to be +chosen. However, the interesting part of a consensus algorithm is the +choosing of a single value. We therefore restrict our attention to +that aspect of consensus algorithms. In practice, given the algorithm +for choosing a value, it is obvious how to implement the Propose and +Learn actions. + +For convenience, we define the macro Choose() that describes the action +of changing the value of `chosen' from {} to {v}, for a +nondeterministically chosen v in the set Value. (There is little +reason to encapsulate such a simple action in a macro; however our +other specs are easier to read when written with such macros, so we +start using them now.) The `when' statement can be executed only when +its condition, chosen = {}, is true. Hence, at most one Choose() +action can be performed in any execution. The `with' statement +executes its body for a nondeterministically chosen v in Value. +Execution of this statement is enabled only if Value is +non-empty--something we do not assume at this point because it is not +required for the safety part of consensus, which is satisfied if no +value is chosen. + +We put the Choose() action inside a `while' statement that loops +forever. Of course, only a single Choose() action can be executed. +The algorithm stops after executing a Choose() action. Technically, +the algorithm deadlocks after executing a Choose() action because +control is at a statement whose execution is never enabled. Formally, +termination is simply deadlock that we want to happen. We could just +as well have omitted the `while' and let the algorithm terminate. +However, adding the `while' loop makes the TLA+ representation of the +algorithm a tiny bit simpler. + +--algorithm Consensus { + variable chosen = {}; + macro Choose() { when chosen = {}; + with (v \in Value) { chosen := {v} } } + { lbl: while (TRUE) { Choose() } + } +} + +The PlusCal translator writes the TLA+ translation of this algorithm +below. The formula Spec is the TLA+ specification described by the +algorithm's code. For now, you should just understand its two +subformulas Init and Next. Formula Init is the initial predicate and +describes all possible initial states of an execution. Formula Next is +the next-state relation; it describes the possible state changes +(changes of the values of variables), where unprimed variables +represent their values in the old state and primed variables represent +their values in the new state. +*****************************************************************************) +\***** BEGIN TRANSLATION +VARIABLE chosen + +vars == << chosen >> + +Init == (* Global variables *) + /\ chosen = {} + +Next == /\ chosen = {} + /\ \E v \in Value: + chosen' = {v} + +Spec == Init /\ [][Next]_vars + +\***** END TRANSLATION +----------------------------------------------------------------------------- +(***************************************************************************) +(* We now prove the safety property that at most one value is chosen. We *) +(* first define the type-correctness invariant TypeOK, and then define Inv *) +(* to be the inductive invariant that asserts TypeOK and that the *) +(* cardinality of the set `chosen' is at most 1. We then prove that, in *) +(* any behavior satisfying the safety specification Spec, the invariant *) +(* Inv is true in all states. This means that at most one value is chosen *) +(* in any behavior. *) +(***************************************************************************) +TypeOK == /\ chosen \subseteq Value + /\ IsFiniteSet(chosen) + +Inv == /\ TypeOK + /\ Cardinality(chosen) \leq 1 + +(***************************************************************************) +(* We now prove that Inv is an invariant, meaning that it is true in every *) +(* state in every behavior. Before trying to prove it, we should first *) +(* use TLC to check that it is true. It's hardly worth bothering to *) +(* either check or prove the obvious fact that Inv is an invariant, but *) +(* it's a nice tiny exercise. Model checking is instantaneous when Value *) +(* is set to any small finite set. *) +(* *) +(* To understand the following proof, you need to understand the formula *) +(* `Spec', which equals *) +(* *) +(* Init /\ [][Next]_vars *) +(* *) +(* where vars is the tuple <> of all variables. It is a *) +(* temporal formula satisfied by a behavior iff the behavior starts in a *) +(* state satisfying Init and such that each step (sequence of states) *) +(* satisfies [Next]_vars, which equals *) +(* *) +(* Next \/ (vars'=vars) *) +(* *) +(* Thus, each step satisfies either Next (so it is a step allowed by the *) +(* next-state relation) or it is a "stuttering step" that leaves all the *) +(* variables unchanged. The reason why a spec must allow stuttering steps *) +(* will become apparent when we prove that a consensus algorithm satisfies *) +(* this specification of consensus. *) +(***************************************************************************) + +(***************************************************************************) +(* The following lemma asserts that Inv is an inductive invariant of the *) +(* next-state action Next. It is the key step in proving that Inv is an *) +(* invariant of (true in every behavior allowed by) specification Spec. *) +(***************************************************************************) +LEMMA InductiveInvariance == + Inv /\ [Next]_vars => Inv' +<1>. SUFFICES ASSUME Inv, [Next]_vars + PROVE Inv' + OBVIOUS +<1>1. CASE Next + \* In the following BY proof, <1>1 denotes the case assumption Next + BY <1>1, FS_EmptySet, FS_AddElement DEF Inv, TypeOK, Next +<1>2. CASE vars' = vars + BY <1>2 DEF Inv, TypeOK, vars +<1>3. QED + BY <1>1, <1>2 DEF Next + +THEOREM Invariance == Spec => []Inv +<1>1. Init => Inv + BY FS_EmptySet DEF Init, Inv, TypeOK +<1>2. QED + BY PTL, <1>1, InductiveInvariance DEF Spec + +----------------------------------------------------------------------------- +(***************************************************************************) +(* We now define LiveSpec to be the algorithm's specification with the *) +(* added fairness condition of weak fairness of the next-state relation, *) +(* which asserts that execution does not stop if some action is enabled. *) +(* The temporal formula Success asserts that some value is eventually *) +(* chosen. Below, we prove that LiveSpec implies Success This means that, *) +(* in every behavior satisfying LiveSpec, some value is chosen. *) +(***************************************************************************) +LiveSpec == Spec /\ WF_vars(Next) +Success == <>(chosen # {}) + +(***************************************************************************) +(* For liveness, we need to assume that there exists at least one value. *) +(***************************************************************************) +ASSUME ValueNonempty == Value # {} + +(***************************************************************************) +(* TLAPS does not yet reason about ENABLED. Therefore, we must omit all *) +(* proofs that involve ENABLED formulas. To perform as much of the proof *) +(* as possible, as much as possible we restrict the use of an ENABLED *) +(* expression to a step asserting that it equals its definition. ENABLED *) +(* A is true of a state s iff there is a state t such that the step s -> t *) +(* satisfies A. It follows from this semantic definition that ENABLED A *) +(* equals the formula obtained by *) +(* *) +(* 1. Expanding all definitions of defined symbols in A until all primes *) +(* are priming variables. *) +(* *) +(* 2. For each primed variable, replacing every instance of that primed *) +(* variable by a new symbol (the same symbol for each primed *) +(* variable). *) +(* *) +(* 3. Existentially quantifying over those new symbols. *) +(***************************************************************************) +LEMMA EnabledDef == + TypeOK => + ((ENABLED <>_vars) <=> (chosen = {})) +<1> DEFINE E == + \E chosenp : + /\ /\ chosen = {} + /\ \E v \in Value: chosenp = {v} + /\ ~ (<> = <>) +<1>1. E = ENABLED <>_vars + \* BY DEF Next, vars (* and def of ENABLED *) + PROOF OMITTED +<1>2. SUFFICES ASSUME TypeOK + PROVE E = (chosen = {}) + BY <1>1, Zenon +<1>3. E = \E chosenp : E!(chosenp)!1 + BY <1>2, Isa DEF TypeOK +<1>4. @ = (chosen = {}) + BY <1>2, ValueNonempty, Zenon DEF TypeOK +<1>5. QED + BY <1>3, <1>4, Zenon + +(***************************************************************************) +(* Here is our proof that Livespec implies Success. It uses the standard *) +(* TLA proof rules. For example RuleWF1 is defined in the TLAPS module to *) +(* be the rule WF1 discussed in *) +(* *) +(* `. AUTHOR = "Leslie Lamport", *) +(* TITLE = "The Temporal Logic of Actions", *) +(* JOURNAL = toplas, *) +(* volume = 16, *) +(* number = 3, *) +(* YEAR = 1994, *) +(* month = may, *) +(* PAGES = "872--923" .' *) +(* *) +(* PTL stands for propositional temporal logic reasoning. We expect that, *) +(* when TLAPS handles temporal reasoning, it will use a decision procedure *) +(* for PTL. *) +(***************************************************************************) +THEOREM LiveSpec => Success +<1>1. []Inv /\ [][Next]_vars /\ WF_vars(Next) => (chosen = {} ~> chosen # {}) + <2>. DEFINE P == chosen = {} + Q == chosen # {} + <2>1. SUFFICES [][Next]_vars /\ WF_vars(Next) => ((Inv /\ P) ~> Q) + BY PTL + <2>2. (Inv /\ P) /\ [Next]_vars => ((Inv' /\ P') \/ Q') + BY InductiveInvariance + <2>3. (Inv /\ P) /\ <>_vars => Q' + BY DEF Inv, Next, vars + <2>4. (Inv /\ P) => ENABLED <>_vars + BY EnabledDef DEF Inv + <2>. HIDE DEF P,Q + <2>. QED + BY <2>2, <2>3, <2>4, PTL +<1>2. (chosen = {} ~> chosen # {}) => ((chosen = {}) => <>(chosen # {})) + BY PTL +<1>3. QED + BY Invariance, <1>1, <1>2, PTL DEF LiveSpec, Spec, Init, Success + +----------------------------------------------------------------------------- +(***************************************************************************) +(* The following theorem is used in the refinement proof in module *) +(* VoteProof. *) +(***************************************************************************) +THEOREM LiveSpecEquals == + LiveSpec <=> Spec /\ ([]<><>_vars \/ []<>(chosen # {})) +<1>1. /\ Spec <=> Spec /\ []TypeOK + /\ LiveSpec <=> LiveSpec /\ []TypeOK + BY Invariance, PTL DEF LiveSpec, Inv +<1>2. (chosen # {}) <=> ~(chosen = {}) + OBVIOUS +<1>3. []TypeOK => (([]<>~ENABLED <>_vars) <=> []<>(chosen # {})) + BY <1>2, EnabledDef, PTL +<1>4. QED + BY <1>1, <1>3, PTL DEF LiveSpec +============================================================================= +\* Modification History +\* Last modified Mon May 11 18:36:27 CEST 2020 by merz +\* Last modified Mon Aug 18 15:00:45 CEST 2014 by tomer +\* Last modified Mon Aug 18 14:58:57 CEST 2014 by tomer +\* Last modified Tue Feb 14 13:35:49 PST 2012 by lamport +\* Last modified Mon Feb 07 14:46:59 PST 2011 by lamport diff --git a/specifications/byzpaxos/PConProof.cfg b/specifications/byzpaxos/PConProof.cfg new file mode 100644 index 00000000..d76fb4bf --- /dev/null +++ b/specifications/byzpaxos/PConProof.cfg @@ -0,0 +1,9 @@ +CONSTANTS + Value = {v1, v2} + Acceptor = {a1, a2, a3} + Quorum = {{a1, a2}, {a1, a3}, {a2, a3}, {a1, a2, a3}} + Ballot = {0, 1, 2} + None = None +INVARIANTS TypeOK PAccInv P1bInv P1cInv P2aInv +SPECIFICATION Spec + diff --git a/specifications/byzpaxos/PConProof.tla b/specifications/byzpaxos/PConProof.tla new file mode 100644 index 00000000..1682138c --- /dev/null +++ b/specifications/byzpaxos/PConProof.tla @@ -0,0 +1,613 @@ +---------------------------- MODULE PConProof ------------------------------- +(***************************************************************************) +(* This is a specification of a variant of the classic Paxos consensus *) +(* algorithm described in *) +(* *) +(* AUTHOR = "Leslie Lamport", *) +(* TITLE = "The Part-Time Parliament", *) +(* journal = ACM Transactions on Computing Systems, *) +(* volume = 16, *) +(* Number = 2, *) +(* Month = may, *) +(* Year = 1998, *) +(* pages = "133--169" *) +(* *) +(* This algorithm was also described without proof in Brian Oki's Ph.D. *) +(* thesis. *) +(* *) +(* It describes the actions that can be performed by leaders, but does not *) +(* introduce explicit leader processes. More precisely, the specification *) +(* is written as if there were a separate leader for each ballot. *) +(* *) +(* This variant of the classic Paxos algorithm is an abstraction of an *) +(* algorithm that is used in *) +(* *) +(* AUTHOR = "Leslie Lamport and Dahlia Malkhi and Lidong Zhou ", *) +(* TITLE = "Vertical Paxos and Primary-Backup Replication", *) +(* Conference = "Proceedings of PODC 2009", *) +(* editor = {Srikanta Tirthapura and Lorenzo Alvisi}, *) +(* publisher = {ACM}, *) +(* YEAR = 2009, *) +(* PAGES = "312--313" *) +(* *) +(* and in *) +(* *) +(* Cheap paxos *) +(* United States Patent 7249280 *) +(* Inventors: Lamport, Leslie B. *) +(* Massa, Michael T. *) +(* Filing Date:06/18/2004 *) +(* *) +(* In the classic Paxos algorithm, the leader sends a phase 2a message for *) +(* a ballot b and value v that instructs acceptors to vote for v in ballot *) +(* b. In terms of implementing the voting algorithm of module VoteProof, *) +(* that 2a message serves two functions: *) +(* *) +(* - It asserts that value v is safe at ballot b, so the acceptor *) +(* can vote for it without violating invariant VInv2 *) +(* *) +(* - It tells the acceptors which single safe value they can vote *) +(* for in ballot b, so they can vote for that value without *) +(* violating VInv3. *) +(* *) +(* The variant of the algorithm we specify here introduces phase 1c *) +(* messages that perform the first function. The phase 2a message serves *) +(* only the first function, being sent only if a 1c message had been sent *) +(* for the value. *) +(* *) +(* This variant of the algorithm is useful when reconfiguration is *) +(* performed by using different sets of acceptors for different ballots. *) +(* The leader propagates knowledge of what values are safe at ballot b so *) +(* that the acceptors in the current configuration are no longer needed to *) +(* determine that information. If the ballot b leader determines that all *) +(* values are safe at b, then it sends a 1c message for every value and *) +(* sends a phase 2a message only when it has a value to propose. The *) +(* presence of the 1c messages removes dependency on the acceptors of *) +(* ballots numbered b or lower for progress. (If the leader determines *) +(* that only a single value is safe at b, then it sends the 1c and 2a *) +(* messages together.) *) +(* *) +(* In the algorithm described here, we do not include reconfiguration. *) +(* Therefore, the sending of a 1c message serves only as a precondition *) +(* for the sending of a 2a message with that value. *) +(* *) +(* Classic Paxos and its variants maintain consensus in the presence of *) +(* omission faults--faults in which a process fails to perform some *) +(* enabled action or a message that is sent fails to be received. The *) +(* safety specification, which is given by the PlusCal code, does not *) +(* require that any action need ever be performed. A process need not *) +(* execute an enabled action. Receipt of a message is modeled by a *) +(* process performing the action enabled by that message having been sent, *) +(* so message loss is also represented by a process not performing an *) +(* enabled action. Thus, failures are never mentioned in the description *) +(* of the algorithm. *) +(***************************************************************************) +EXTENDS Integers, TLAPS +----------------------------------------------------------------------------- +(***************************************************************************) +(* The constant parameters and the set Ballots are the same as in the *) +(* voting algorithm. *) +(***************************************************************************) +CONSTANT Value, Acceptor, Quorum + +ASSUME QA == /\ \A Q \in Quorum : Q \subseteq Acceptor + /\ \A Q1, Q2 \in Quorum : Q1 \cap Q2 # {} + +Ballot == Nat + +(***************************************************************************) +(* We are going to have a leader process for each ballot and an acceptor *) +(* process for each acceptor. So we can use the ballot numbers and the *) +(* acceptors themselves as the identifiers for these processes, we assume *) +(* that the set of ballots and the set of acceptors are disjoint. For *) +(* good measure, we also assume that -1 is not an acceptor, although that *) +(* is probably not necessary. *) +(***************************************************************************) +ASSUME BallotAssump == (Ballot \cup {-1}) \cap Acceptor = {} + +(***************************************************************************) +(* We define None to be an unspecified value that is not in the set Value. *) +(***************************************************************************) +None == CHOOSE v : v \notin Value + +(***************************************************************************) +(* This is a message-passing algorithm, so we begin by defining the set *) +(* Message of all possible messages. The messages are explained below *) +(* with the actions that send them. A message m with m.type = "1a" is *) +(* called a 1a message, and similarly for the other message types. *) +(***************************************************************************) +Message == [type : {"1a"}, bal : Ballot] + \cup [type : {"1b"}, acc : Acceptor, bal : Ballot, + mbal : Ballot \cup {-1}, mval : Value \cup {None}] + \cup [type : {"1c"}, bal : Ballot, val : Value] + \cup [type : {"2a"}, bal : Ballot, val : Value] + \cup [type : {"2b"}, acc : Acceptor, bal : Ballot, val : Value] +----------------------------------------------------------------------------- + + +(***************************************************************************) +(* The algorithm is easiest to understand in terms of the set msgs of all *) +(* messages that have ever been sent. A more accurate model would use one *) +(* or more variables to represent the messages actually in transit, and it *) +(* would include actions representing message loss and duplication as well *) +(* as message receipt. *) +(* *) +(* In the current spec, there is no need to model message loss explicitly. *) +(* The safety part of the spec says only what messages may be received and *) +(* does not assert that any message actually is received. Thus, there is *) +(* no difference between a lost message and one that is never received. *) +(* The liveness property of the spec will make it clear what messages must *) +(* be received (and hence either not lost or successfully retransmitted if *) +(* lost) to guarantee progress. *) +(* *) +(* Another advantage of maintaining the set of all messages that have ever *) +(* been sent is that it allows us to define the state function `votes' *) +(* that implements the variable of the same name in the voting algorithm *) +(* without having to introduce a history variable. *) +(***************************************************************************) +(*********** + +In addition to the variable msgs, the algorithm uses four variables +whose values are arrays indexed by acceptor, where for any acceptor +`a': + + maxBal[a] The largest ballot number in which `a' has participated + + maxVBal[a] The largest ballot number in which a has voted, or -1 + if it has never voted. + + maxVVal[a] If `a' has voted, then this is the value it voted for in + ballot maxVBal; otherwise it equals None. + +As in the voting algorithm, an execution of the algorithm consists of an +execution of zero or more ballots. Different ballots may be in progress +concurrently, and ballots may not complete (and need not even start). +A ballot b consists of the following actions (which need not all occur +in the indicated order). + + Phase1a : The leader sends a 1a message for ballot b + + Phase1b : If maxBal[a] < b, an acceptor `a' responds to the 1a message by + setting maxBal[a] to b and sending a 1b message to the leader + containing the values of maxVBal[a] and maxVVal[a]. + + Phase1c : When the leader has received ballot-b 1b messages from a + quorum, it determines some set of values that are safe + at b and sends 1c messages for them. + + Phase2a : The leader sends a 2a message for some value for which it has + already sent a ballot-b 1c message. + + Phase2b : Upon receipt of the 2a message, if maxBal[a] =< b, an + acceptor `a' sets maxBal[a] and maxVBal[a] to b, sets + maxVVal[a] to the value in the 2a message, and votes for + that value in ballot b by sending the appropriate 2b + message. + +Here is the PlusCal code for the algorithm, which we call PCon. + +--algorithm PCon { + variables maxBal = [a \in Acceptor |-> -1] , + maxVBal = [a \in Acceptor |-> -1] , + maxVVal = [a \in Acceptor |-> None] , + msgs = {} + define { + sentMsgs(t, b) == {m \in msgs : (m.type = t) /\ (m.bal = b)} + + (***********************************************************************) + (* We define ShowsSafeAt so that ShowsSafeAt(Q, b, v) is true for a *) + (* quorum Q iff msgs contain ballot-b 1b messages from the acceptors *) + (* in Q showing that v is safe at b. *) + (***********************************************************************) + ShowsSafeAt(Q, b, v) == + LET Q1b == {m \in sentMsgs("1b", b) : m.acc \in Q} + IN /\ \A a \in Q : \E m \in Q1b : m.acc = a + /\ \/ \A m \in Q1b : m.mbal = -1 + \/ \E m1c \in msgs : + /\ m1c = [type |-> "1c", bal |-> m1c.bal, val |-> v] + /\ \A m \in Q1b : /\ m1c.bal \geq m.mbal + /\ (m1c.bal = m.mbal) => (m.mval = v) + + } + (*************************************************************************) + (* The following two macros send a message and a set of messages, *) + (* respectively. These macros are so simple that they're hardly worth *) + (* introducing, but they do make the processes a little easier to read. *) + (*************************************************************************) + macro SendMessage(m) { msgs := msgs \cup {m} } + macro SendSetOfMessages(S) { msgs := msgs \cup S } + + (*************************************************************************) + (* The Actions *) + (* As before, we describe each action as a macro. *) + (* *) + (* The leader for process `self' can execute a Phase1a() action, which *) + (* sends the ballot `self' 1a message. *) + (*************************************************************************) + macro Phase1a() { SendMessage([type |-> "1a", bal |-> self])} + + (*************************************************************************) + (* Acceptor `self' can perform a Phase1b(b) action, which is enabled iff *) + (* b > maxBal[self]. The action sets maxBal[self] to b and sends a *) + (* phase 1b message to the leader containing the values of maxVBal[self] *) + (* and maxVVal[self]. *) + (*************************************************************************) + macro Phase1b(b) { + when (b > maxBal[self]) /\ (sentMsgs("1a", b) # {}); + maxBal[self] := b; + SendMessage([type |-> "1b", acc |-> self, bal |-> b, + mbal |-> maxVBal[self], mval |-> maxVVal[self]]) ; + } + + (*************************************************************************) + (* The ballot `self' leader can perform a Phase1c(S) action, which sends *) + (* a set S of 1c messages indicating that the value in the val field of *) + (* each of them is safe at ballot b. In practice, S will either contain *) + (* a single message, or else will have a message for each possible *) + (* value, indicating that all values are safe. In the first case, the *) + (* leader will immediately send a 2a message with the value contained in *) + (* that single message. (Both logical messages will be sent in the same *) + (* physical message.) In the latter case, the leader is informing the *) + (* acceptors that all values are safe. (All those logical messages *) + (* will, of course, be encoded in a single physical message.) *) + (*************************************************************************) + macro Phase1c(S) { + when \A v \in S : \E Q \in Quorum : ShowsSafeAt(Q, self, v) ; + SendSetOfMessages({[type |-> "1c", bal |-> self, val |-> v] : v \in S}) + } + + (*************************************************************************) + (* The ballot `self' leader can perform a Phase2a(v) action, sending a *) + (* 2a message for value v, if it has not already sent a 2a message (for *) + (* this ballot) and it has sent a ballot `self' 1c message with val *) + (* field v. *) + (*************************************************************************) + macro Phase2a(v) { + when /\ sentMsgs("2a", self) = {} + /\ [type |-> "1c", bal |-> self, val |-> v] \in msgs ; + SendMessage([type |-> "2a", bal |-> self, val |-> v]) + } + + (*************************************************************************) + (* The Phase2b(b) action is executed by acceptor `self' in response to a *) + (* ballot-b 2a message. Note this action can be executed multiple times *) + (* by the acceptor, but after the first one, all subsequent executions *) + (* are stuttering steps that do not change the value of any variable. *) + (*************************************************************************) + macro Phase2b(b) { + when b \geq maxBal[self] ; + with (m \in sentMsgs("2a", b)) { + maxBal[self] := b ; + maxVBal[self] := b ; + maxVVal[self] := m.val; + SendMessage([type |-> "2b", acc |-> self, bal |-> b, val |-> m.val]) + } + } + + (*************************************************************************) + (* An acceptor performs the body of its `while' loop as a single atomic *) + (* action by nondeterministically choosing a ballot in which its Phase1b *) + (* or Phase2b action is enabled and executing that enabled action. If *) + (* no such action is enabled, the acceptor does nothing. *) + (*************************************************************************) + process (acceptor \in Acceptor) { + acc: while (TRUE) { + with (b \in Ballot) { either Phase1b(b) or Phase2b(b) + } + } + } + + (*************************************************************************) + (* The leader of a ballot nondeterministically chooses one of its *) + (* actions that is enabled (and the argument for which it is enabled) *) + (* and performs it atomically. It does nothing if none of its actions *) + (* is enabled. *) + (*************************************************************************) + process (leader \in Ballot) { + ldr: while (TRUE) { + either Phase1a() + or with (S \in SUBSET Value) { Phase1c(S) } + or with (v \in Value) { Phase2a(v) } + } + } + +} + +The translator produces the following TLA+ specification of the algorithm. +Some blank lines have been deleted. +************) +\* BEGIN TRANSLATION +VARIABLES maxBal, maxVBal, maxVVal, msgs + +(* define statement *) +sentMsgs(t, b) == {m \in msgs : (m.type = t) /\ (m.bal = b)} + +ShowsSafeAt(Q, b, v) == + LET Q1b == {m \in sentMsgs("1b", b) : m.acc \in Q} + IN /\ \A a \in Q : \E m \in Q1b : m.acc = a + /\ \/ \A m \in Q1b : m.mbal = -1 + \/ \E m1c \in msgs : + /\ m1c = [type |-> "1c", bal |-> m1c.bal, val |-> v] + /\ \A m \in Q1b : /\ m1c.bal \geq m.mbal + /\ (m1c.bal = m.mbal) => (m.mval = v) + + +vars == << maxBal, maxVBal, maxVVal, msgs >> + +ProcSet == (Acceptor) \cup (Ballot) + +Init == (* Global variables *) + /\ maxBal = [a \in Acceptor |-> -1] + /\ maxVBal = [a \in Acceptor |-> -1] + /\ maxVVal = [a \in Acceptor |-> None] + /\ msgs = {} + +acceptor(self) == \E b \in Ballot: + \/ /\ (b > maxBal[self]) /\ (sentMsgs("1a", b) # {}) + /\ maxBal' = [maxBal EXCEPT ![self] = b] + /\ msgs' = (msgs \cup {([type |-> "1b", acc |-> self, bal |-> b, + mbal |-> maxVBal[self], mval |-> maxVVal[self]])}) + /\ UNCHANGED <> + \/ /\ b \geq maxBal[self] + /\ \E m \in sentMsgs("2a", b): + /\ maxBal' = [maxBal EXCEPT ![self] = b] + /\ maxVBal' = [maxVBal EXCEPT ![self] = b] + /\ maxVVal' = [maxVVal EXCEPT ![self] = m.val] + /\ msgs' = (msgs \cup {([type |-> "2b", acc |-> self, bal |-> b, val |-> m.val])}) + + +leader(self) == /\ \/ /\ msgs' = (msgs \cup {([type |-> "1a", bal |-> self])}) + \/ /\ \E S \in SUBSET Value: + /\ \A v \in S : \E Q \in Quorum : ShowsSafeAt(Q, self, v) + /\ msgs' = (msgs \cup ({[type |-> "1c", bal |-> self, val |-> v] : v \in S})) + \/ /\ \E v \in Value: + /\ /\ sentMsgs("2a", self) = {} + /\ [type |-> "1c", bal |-> self, val |-> v] \in msgs + /\ msgs' = (msgs \cup {([type |-> "2a", bal |-> self, val |-> v])}) + /\ UNCHANGED << maxBal, maxVBal, maxVVal >> + + +Next == (\E self \in Acceptor: acceptor(self)) + \/ (\E self \in Ballot: leader(self)) + +Spec == Init /\ [][Next]_vars + +\* END TRANSLATION +----------------------------------------------------------------------------- +(***************************************************************************) +(* We now rewrite the next-state relation in a way that makes it easier to *) +(* use in a proof. We start by defining the formulas representing the *) +(* individual actions. We then use them to define the formula TLANext, *) +(* which is the next-state relation we would have written had we specified *) +(* the algorithm directly in TLA+ rather than in PlusCal. *) +(***************************************************************************) +Phase1a(self) == + /\ msgs' = (msgs \cup {[type |-> "1a", bal |-> self]}) + /\ UNCHANGED << maxBal, maxVBal, maxVVal >> + +Phase1c(self, S) == + /\ \A v \in S : \E Q \in Quorum : ShowsSafeAt(Q, self, v) + /\ msgs' = (msgs \cup {[type |-> "1c", bal |-> self, val |-> v] : v \in S}) + /\ UNCHANGED << maxBal, maxVBal, maxVVal >> + +Phase2a(self, v) == + /\ sentMsgs("2a", self) = {} + /\ [type |-> "1c", bal |-> self, val |-> v] \in msgs + /\ msgs' = (msgs \cup {[type |-> "2a", bal |-> self, val |-> v]}) + /\ UNCHANGED << maxBal, maxVBal, maxVVal >> + +Phase1b(self, b) == + /\ b > maxBal[self] + /\ sentMsgs("1a", b) # {} + /\ maxBal' = [maxBal EXCEPT ![self] = b] + /\ msgs' = msgs \cup {[type |-> "1b", acc |-> self, bal |-> b, + mbal |-> maxVBal[self], mval |-> maxVVal[self]]} + /\ UNCHANGED <> + +Phase2b(self, b) == + /\ b \geq maxBal[self] + /\ \E m \in sentMsgs("2a", b): + /\ maxBal' = [maxBal EXCEPT ![self] = b] + /\ maxVBal' = [maxVBal EXCEPT ![self] = b] + /\ maxVVal' = [maxVVal EXCEPT ![self] = m.val] + /\ msgs' = (msgs \cup {[type |-> "2b", acc |-> self, + bal |-> b, val |-> m.val]}) + +TLANext == + \/ \E self \in Acceptor : + \E b \in Ballot : \/ Phase1b(self, b) + \/ Phase2b(self,b) + \/ \E self \in Ballot : + \/ Phase1a(self) + \/ \E S \in SUBSET Value : Phase1c(self, S) + \/ \E v \in Value : Phase2a(self, v) + +(***************************************************************************) +(* The following theorem specifies the relation between the next-state *) +(* relation Next obtained by translating the PlusCal code and the *) +(* next-state relation TLANext. *) +(***************************************************************************) +THEOREM NextDef == (Next <=> TLANext) +<1>2. ASSUME NEW self \in Acceptor + PROVE acceptor(self) <=> TLANext!1!(self) + BY <1>2, BallotAssump DEF acceptor, ProcSet, Phase1b, Phase2b +<1>3. ASSUME NEW self \in Ballot + PROVE leader(self) <=> TLANext!2!(self) + BY <1>3, BallotAssump, Zenon DEF leader, ProcSet, Phase1a, Phase1c, Phase2a +<1>4. QED + BY <1>2, <1>3 DEF Next, TLANext +----------------------------------------------------------------------------- +(***************************************************************************) +(* The type invariant. *) +(***************************************************************************) +TypeOK == /\ maxBal \in [Acceptor -> Ballot \cup {-1}] + /\ maxVBal \in [Acceptor -> Ballot \cup {-1}] + /\ maxVVal \in [Acceptor -> Value \cup {None}] + /\ msgs \subseteq Message + +(***************************************************************************) +(* Here is the definition of the state-function `chosen' that implements *) +(* the state-function of the same name in the voting algorithm. *) +(***************************************************************************) +chosen == {v \in Value : \E Q \in Quorum, b \in Ballot : + \A a \in Q : \E m \in msgs : /\ m.type = "2b" + /\ m.acc = a + /\ m.bal = b + /\ m.val = v} +---------------------------------------------------------------------------- +(***************************************************************************) +(* We now define the refinement mapping under which this algorithm *) +(* implements the specification in module Voting. *) +(***************************************************************************) + +(***************************************************************************) +(* As we observed, votes are registered by sending phase 2b messages. So *) +(* the array `votes' describing the votes cast by the acceptors is defined *) +(* as follows. *) +(***************************************************************************) +votes == [a \in Acceptor |-> + {<> : m \in {mm \in msgs: /\ mm.type = "2b" + /\ mm.acc = a }}] + +(***************************************************************************) +(* We now instantiate module Voting, substituting: *) +(* *) +(* - The constants Value, Acceptor, and Quorum declared in this module *) +(* for the corresponding constants of that module Voting. *) +(* *) +(* - The variable maxBal and the defined state function `votes' for the *) +(* correspondingly-named variables of module Voting. *) +(***************************************************************************) +V == INSTANCE VoteProof + +----------------------------------------------------------------------------- +(***************************************************************************) +(* We now define PInv to be what I believe to be an inductive invariant *) +(* and assert the theorems for proving that this algorithm implements the *) +(* voting algorithm under the refinement mapping specified by the INSTANCE *) +(* statement. Whether PInv really is an inductive invariant will be *) +(* determined only by a rigorous proof. *) +(***************************************************************************) +PAccInv == \A a \in Acceptor : + /\ maxBal[a] >= maxVBal[a] + /\ \A b \in (maxVBal[a]+1)..(maxBal[a]-1) : V!DidNotVoteIn(a,b) + /\ (maxVBal[a] # -1) => V!VotedFor(a, maxVBal[a], maxVVal[a]) + +P1bInv == \A m \in msgs : + (m.type = "1b") => + /\ (maxBal[m.acc] >= m.bal) /\ (m.bal > m.mbal) + /\ \A b \in (m.mbal+1)..(m.bal-1) : V!DidNotVoteIn(m.acc,b) + +P1cInv == \A m \in msgs : (m.type = "1c") => V!SafeAt(m.bal, m.val) + +P2aInv == \A m \in msgs : + (m.type = "2a") => \E m1c \in msgs : /\ m1c.type = "1c" + /\ m1c.bal = m.bal + /\ m1c.val = m.val +(***************************************************************************) +(* The following theorem is interesting in its own right. It essentially *) +(* asserts the correctness of the definition of ShowsSafeAt. *) +(***************************************************************************) +THEOREM PT1 == TypeOK /\ P1bInv /\ P1cInv => + \A Q \in Quorum, b \in Ballot, v \in Value : + ShowsSafeAt(Q, b, v) => V!SafeAt(b, v) + +PInv == TypeOK /\ PAccInv /\ P1bInv /\ P1cInv /\ P2aInv + +THEOREM Invariance == Spec => []PInv + +AbstractSpec == V!Spec +THEOREM Implementation == Spec => V!Spec + +(***************************************************************************) +(* The following result shows that our definition of `chosen' is the *) +(* correct one, because it implements the state-function `chosen' of the *) +(* voting algorithm. *) +(***************************************************************************) +THEOREM Spec => [](chosen = V!chosen) + +(***************************************************************************) +(* The four theorems above have been checked by TLC for a model with 3 *) +(* acceptors, 2 values, and 3 ballot numbers. Theorem PT1 was checked as *) +(* an invariant, therefore checking only that it is true for all reachable *) +(* states. This model is large enough that it would most likely have *) +(* revealed any "coding" errors in the algorithm. We believe that the *) +(* algorithm is well-enough understood that it is unlikely to contain any *) +(* fundamental errors. *) +(***************************************************************************) +============================================================================= +\* Modification History +\* Last modified Fri May 22 09:20:18 CEST 2020 by merz +\* Last modified Fri Jul 15 11:31:15 PDT 2011 by lamport + +----------------------------------------------------------------------------- +(***************************************************************************) +(* Liveness *) +(* *) +(* The liveness property satisfied by PCon (and classic Paxos) is: *) +(* *) +(* If there is some ballot b and quorum Q such that *) +(* *) +(* 1. No phase 1a messages (a) have been or (b) ever will be sent for any *) +(* ballot number greater than b. *) +(* *) +(* 2. The ballot b leader eventually sends a phase 1a message for ballot *) +(* b. *) +(* *) +(* 3. Each acceptor in Q eventually responds to ballot b messages sent *) +(* by the ballot b leader--which implies that it eventually receives *) +(* those messages. *) +(* *) +(* 4. The ballot b leader eventually executes its Phase2a action for *) +(* ballot b if it can. *) +(* *) +(* then some value is eventually chosen. *) +(* *) +(* Note that Phase2a(b) is enabled if msgs contains a ballot b phase 1b *) +(* message from every acceptor in Q. Hence, 4 implies that if the leader *) +(* eventually receives those messages, then it must perform its Phase2a(b) *) +(* action. (It might perform that action before it receives those *) +(* messages if it has received phase 1b messages from all the acceptors in *) +(* a different quorum.) *) +(***************************************************************************) +THEOREM Liveness == + Spec => \A b \in Ballot, Q \in Quorum : + ( ( /\ (*********************************************************) + (* Assumption 1a. *) + (*********************************************************) + \A m \in msgs : (m.type = "1a") => (m.bal < b) + /\ (*********************************************************) + (* Assumption 1b. *) + (*********************************************************) + \A c \in Ballot : (c > b) => [][~Phase1a(c)]_vars + /\ (*********************************************************) + (* Assumption 2. *) + (*********************************************************) + WF_vars(Phase1a(b)) + /\ (*********************************************************) + (* Assumption 4. *) + (*********************************************************) + WF_vars(\E v \in Value : Phase2a(b, v)) + /\ (*********************************************************) + (* Assumption 3. *) + (*********************************************************) + \A a \in Q : /\ WF_vars(Phase1b(a, b)) + /\ WF_vars(Phase2b(a, b)) + ) + ~> (chosen # {}) ) +============================================================================= + +\* The following is used to check theorem Liveness + +CONSTANTS bb, QQ + +CSpec == /\ Init + /\ [][ /\ Next + /\ \A c \in Ballot : (c > bb) => ~Phase1a(c)]_vars + /\ WF_vars(Phase1a(bb)) + /\ WF_vars(\E v \in Value : Phase2a(bb, v)) + /\ \A a \in QQ : /\ WF_vars(Phase1bForBallot(a, bb)) + /\ WF_vars(Phase2bForBallot(a, bb)) + +CLiveness == (\A m \in msgs : (m.type = "1a") => (m.bal < bb))~>(chosen # {}) + diff --git a/specifications/byzpaxos/README.md b/specifications/byzpaxos/README.md index 9b13ab4a..59d6fd9f 100644 --- a/specifications/byzpaxos/README.md +++ b/specifications/byzpaxos/README.md @@ -1,8 +1,6 @@ -#### byzpaxos -- Specification's authors: Leslie Lamport? -- Original paper: Lamport, Leslie. Byzantizing Paxos by refinement. International Symposium on Distributed Computing. Springer, Berlin, Heidelberg, 2011. -- Extended modules: Int, FinSet -- Computation models: Byzantine -- Some properties checked with TLC: Safety +# Byzantizing Paxos by Refinement +Authored by Leslie Lamport + +http://lamport.azurewebsites.net/tla/byzpaxos.html diff --git a/specifications/byzpaxos/VoteProof.cfg b/specifications/byzpaxos/VoteProof.cfg new file mode 100644 index 00000000..23ffb857 --- /dev/null +++ b/specifications/byzpaxos/VoteProof.cfg @@ -0,0 +1,10 @@ +CONSTANTS + Value = {v1, v2} + Acceptor = {a1, a2, a3} + Quorum = {{a1, a2}, {a1, a3}, {a2, a3}, {a1, a2, a3}} + Ballot = {0, 1, 2} +INVARIANTS TypeOK VInv1 VInv2 VInv3 VInv4 +PROPERTIES Refines +SPECIFICATION Spec +CHECK_DEADLOCK FALSE + diff --git a/specifications/byzpaxos/VoteProof.tla b/specifications/byzpaxos/VoteProof.tla new file mode 100644 index 00000000..4cfe4097 --- /dev/null +++ b/specifications/byzpaxos/VoteProof.tla @@ -0,0 +1,1496 @@ +----------------------------- MODULE VoteProof ------------------------------ +(***************************************************************************) +(* This is a high-level consensus algorithm in which a set of processes *) +(* called `acceptors' cooperatively choose a value. The algorithm uses *) +(* numbered ballots, where a ballot is a round of voting. Acceptors cast *) +(* votes in ballots, casting at most one vote per ballot. A value is *) +(* chosen when a large enough set of acceptors, called a `quorum', have *) +(* all voted for the same value in the same ballot. *) +(* *) +(* Ballots are not executed in order. Different acceptors may be *) +(* concurrently performing actions for different ballots. *) +(***************************************************************************) +EXTENDS Integers, NaturalsInduction, FiniteSets, FiniteSetTheorems, + WellFoundedInduction, TLC, TLAPS + +CONSTANT Value, \* As in module Consensus, the set of choosable values. + Acceptor, \* The set of all acceptors. + Quorum \* The set of all quorums. + +(***************************************************************************) +(* The following assumption asserts that a quorum is a set of acceptors, *) +(* and the fundamental assumption we make about quorums: any two quorums *) +(* have a non-empty intersection. *) +(***************************************************************************) +ASSUME QA == /\ \A Q \in Quorum : Q \subseteq Acceptor + /\ \A Q1, Q2 \in Quorum : Q1 \cap Q2 # {} + +THEOREM QuorumNonEmpty == \A Q \in Quorum : Q # {} +PROOF BY QA +----------------------------------------------------------------------------- +(***************************************************************************) +(* Ballot is the set of all ballot numbers. For simplicity, we let it be *) +(* the set of natural numbers. However, we write Ballot for that set to *) +(* make it clear what the function of those natural numbers are. *) +(* *) +(* The algorithm and its refinements work with Ballot any set with minimal *) +(* element 0, -1 not an element of Ballot, and a well-founded total order *) +(* < on Ballot \cup {-1} with minimal element -1, and 0 < b for all *) +(* non-zero b in Ballot. In the proof, any set of the form i..j must be *) +(* replaced by the set of all elements b in Ballot \cup {-1} with i \leq b *) +(* \leq j, and i..(j-1) by the set of such b with i \leq b < j. *) +(***************************************************************************) +Ballot == Nat +----------------------------------------------------------------------------- +(***************************************************************************) +(* In the algorithm, each acceptor can cast one or more votes, where each *) +(* vote cast by an acceptor has the form <> indicating that the *) +(* acceptor has voted for value v in ballot b. A value is chosen if a *) +(* quorum of acceptors have voted for it in the same ballot. *) +(* *) +(* The algorithm uses two variables, `votes' and `maxBal', both arrays *) +(* indexed by acceptor. Their meanings are: *) +(* *) +(* votes[a] - The set of votes cast by acceptor `a'. *) +(* *) +(* maxBal[a] - The number of the highest-numbered ballot in which `a' *) +(* has cast a vote, or -1 if it has not yet voted. *) +(* *) +(* The algorithm does not let acceptor `a' vote in any ballot less than *) +(* maxBal[a]. *) +(* *) +(* We specify our algorithm by the following PlusCal algorithm. The *) +(* specification Spec defined by this algorithm describes only the safety *) +(* properties of the algorithm. In other words, it specifies what steps *) +(* the algorithm may take. It does not require that any (non-stuttering) *) +(* steps be taken. We prove that this specification Spec implements the *) +(* specification Spec of module Consensus under a refinement mapping *) +(* defined below. This shows that the safety properties of the voting *) +(* algorithm (and hence the algorithm with additional liveness *) +(* requirements) imply the safety properties of the Consensus *) +(* specification. Liveness is discussed later. *) +(***************************************************************************) + +(*************************** +--algorithm Voting { + variables votes = [a \in Acceptor |-> {}], + maxBal = [a \in Acceptor |-> -1]; + define { + (************************************************************************) + (* We now define the operator SafeAt so SafeAt(b, v) is function of the *) + (* state that equals TRUE if no value other than v has been chosen or *) + (* can ever be chosen in the future (because the values of the *) + (* variables votes and maxBal are such that the algorithm does not *) + (* allow enough acceptors to vote for it). We say that value v is safe *) + (* at ballot number b iff Safe(b, v) is true. We define Safe in terms *) + (* of the following two operators. *) + (* *) + (* Note: This definition is weaker than would be necessary to allow a *) + (* refinement of ordinary Paxos consensus, since it allows different *) + (* quorums to "cooperate" in determining safety at b. This is used in *) + (* algorithms like Vertical Paxos that are designed to allow *) + (* reconfiguration within a single consensus instance, but not in *) + (* ordinary Paxos. See *) + (* *) + (* AUTHOR = "Leslie Lamport and Dahlia Malkhi and Lidong Zhou ", *) + (* TITLE = "Vertical Paxos and Primary-Backup Replication", *) + (* Journal = "ACM SIGACT News (Distributed Computing Column)", *) + (* editor = {Srikanta Tirthapura and Lorenzo Alvisi}, *) + (* booktitle = {PODC}, *) + (* publisher = {ACM}, *) + (* YEAR = 2009, *) + (* PAGES = "312--313" *) + (************************************************************************) + VotedFor(a, b, v) == <> \in votes[a] + (**********************************************************************) + (* True iff acceptor a has voted for v in ballot b. *) + (**********************************************************************) + DidNotVoteIn(a, b) == \A v \in Value : ~ VotedFor(a, b, v) + + (************************************************************************) + (* We now define SafeAt. We define it recursively. The nicest *) + (* definition is *) + (* *) + (* RECURSIVE SafeAt(_, _) *) + (* SafeAt(b, v) == *) + (* \/ b = 0 *) + (* \/ \E Q \in Quorum : *) + (* /\ \A a \in Q : maxBal[a] \geq b *) + (* /\ \E c \in -1..(b-1) : *) + (* /\ (c # -1) => /\ SafeAt(c, v) *) + (* /\ \A a \in Q : *) + (* \A w \in Value : *) + (* VotedFor(a, c, w) => (w = v) *) + (* /\ \A d \in (c+1)..(b-1), a \in Q : DidNotVoteIn(a, d) *) + (* *) + (* However, TLAPS does not currently support recursive operator *) + (* definitions. We therefore define it as follows using a recursive *) + (* function definition. *) + (************************************************************************) + SafeAt(b, v) == + LET SA[bb \in Ballot] == + (****************************************************************) + (* This recursively defines SA[bb] to equal SafeAt(bb, v). *) + (****************************************************************) + \/ bb = 0 + \/ \E Q \in Quorum : + /\ \A a \in Q : maxBal[a] \geq bb + /\ \E c \in -1..(bb-1) : + /\ (c # -1) => /\ SA[c] + /\ \A a \in Q : + \A w \in Value : + VotedFor(a, c, w) => (w = v) + /\ \A d \in (c+1)..(bb-1), a \in Q : DidNotVoteIn(a, d) + IN SA[b] + } + (*************************************************************************) + (* There are two possible actions that an acceptor can perform, each *) + (* defined by a macro. In these macros, `self' is the acceptor that is *) + (* to perform the action. The first action, IncreaseMaxBal(b) allows *) + (* acceptor `self' to set maxBal[self] to b if b is greater than the *) + (* current value of maxBal[self]. *) + (*************************************************************************) + macro IncreaseMaxBal(b) { + when b > maxBal[self] ; + maxBal[self] := b + } + + (*************************************************************************) + (* Action VoteFor(b, v) allows acceptor `self' to vote for value v in *) + (* ballot b if its `when' condition is satisfied. *) + (*************************************************************************) + macro VoteFor(b, v) { + when /\ maxBal[self] \leq b + /\ DidNotVoteIn(self, b) + /\ \A p \in Acceptor \ {self} : + \A w \in Value : VotedFor(p, b, w) => (w = v) + /\ SafeAt(b, v) ; + votes[self] := votes[self] \cup {<>}; + maxBal[self] := b + } + + (*************************************************************************) + (* The following process declaration asserts that every process `self' *) + (* in the set Acceptor executes its body, which loops forever *) + (* nondeterministically choosing a Ballot b and executing either an *) + (* IncreaseMaxBal(b) action or nondeterministically choosing a value v *) + (* and executing a VoteFor(b, v) action. The single label indicates *) + (* that an entire execution of the body of the `while' loop is performed *) + (* as a single atomic action. *) + (* *) + (* From this intuitive description of the process declaration, one might *) + (* think that a process could be deadlocked by choosing a ballot b in *) + (* which neither an IncreaseMaxBal(b) action nor any VoteFor(b, v) *) + (* action is enabled. An examination of the TLA+ translation (and an *) + (* elementary knowledge of the meaning of existential quantification) *) + (* shows that this is not the case. You can think of all possible *) + (* choices of b and of v being examined simultaneously, and one of the *) + (* choices for which a step is possible being made. *) + (*************************************************************************) + process (acceptor \in Acceptor) { + acc : while (TRUE) { + with (b \in Ballot) { + either IncreaseMaxBal(b) + or with (v \in Value) { VoteFor(b, v) } + } + } + } +} + +The following is the TLA+ specification produced by the translation. +Blank lines, produced by the translation because of the comments, have +been deleted. +****************************) +\* BEGIN TRANSLATION +VARIABLES votes, maxBal + +(* define statement *) +VotedFor(a, b, v) == <> \in votes[a] + +DidNotVoteIn(a, b) == \A v \in Value : ~ VotedFor(a, b, v) + +SafeAt(b, v) == + LET SA[bb \in Ballot] == + \/ bb = 0 + \/ \E Q \in Quorum : + /\ \A a \in Q : maxBal[a] \geq bb + /\ \E c \in -1..(bb-1) : + /\ (c # -1) => /\ SA[c] + /\ \A a \in Q : + \A w \in Value : + VotedFor(a, c, w) => (w = v) + /\ \A d \in (c+1)..(bb-1), a \in Q : DidNotVoteIn(a, d) + IN SA[b] + +vars == << votes, maxBal >> + +ProcSet == (Acceptor) + +Init == (* Global variables *) + /\ votes = [a \in Acceptor |-> {}] + /\ maxBal = [a \in Acceptor |-> -1] + +acceptor(self) == \E b \in Ballot: + \/ /\ b > maxBal[self] + /\ maxBal' = [maxBal EXCEPT ![self] = b] + /\ UNCHANGED votes + \/ /\ \E v \in Value: + /\ /\ maxBal[self] \leq b + /\ DidNotVoteIn(self, b) + /\ \A p \in Acceptor \ {self} : + \A w \in Value : VotedFor(p, b, w) => (w = v) + /\ SafeAt(b, v) + /\ votes' = [votes EXCEPT ![self] = votes[self] \cup {<>}] + /\ maxBal' = [maxBal EXCEPT ![self] = b] + + +Next == (\E self \in Acceptor: acceptor(self)) + +Spec == Init /\ [][Next]_vars + +\* END TRANSLATION +----------------------------------------------------------------------------- +(***************************************************************************) +(* To reason about a recursively-defined operator, one must prove a *) +(* theorem about it. In particular, to reason about SafeAt, we need to *) +(* prove that SafeAt(b, v) equals the right-hand side of its definition, *) +(* for b \in Ballot and v \in Value. This is not automatically true for a *) +(* recursive definition. For example, from the recursive definition *) +(* *) +(* Silly[n \in Nat] == CHOOSE v : v # Silly[n] *) +(* *) +(* we cannot deduce that *) +(* *) +(* Silly[42] = CHOOSE v : v # Silly[42] *) +(* *) +(* (From that, we could easily deduce Silly[42] # Silly[42].) *) +(***************************************************************************) + +(***************************************************************************) +(* Here is the theorem that essentially asserts that SafeAt(b, v) equals *) +(* the right-hand side of its definition. *) +(***************************************************************************) +THEOREM SafeAtProp == + \A b \in Ballot, v \in Value : + SafeAt(b, v) <=> + \/ b = 0 + \/ \E Q \in Quorum : + /\ \A a \in Q : maxBal[a] \geq b + /\ \E c \in -1..(b-1) : + /\ (c # -1) => /\ SafeAt(c, v) + /\ \A a \in Q : + \A w \in Value : + VotedFor(a, c, w) => (w = v) + /\ \A d \in (c+1)..(b-1), a \in Q : DidNotVoteIn(a, d) +<1>1. SUFFICES ASSUME NEW v \in Value + PROVE \A b \in Ballot : SafeAtProp!(b, v) + BY Zenon +<1> USE DEF Ballot +<1> DEFINE Def(SA, bb) == + \/ bb = 0 + \/ \E Q \in Quorum : + /\ \A a \in Q : maxBal[a] \geq bb + /\ \E c \in -1..(bb-1) : + /\ (c # -1) => /\ SA[c] + /\ \A a \in Q : + \A w \in Value : + VotedFor(a, c, w) => (w = v) + /\ \A d \in (c+1)..(bb-1), a \in Q : DidNotVoteIn(a, d) + SA[bb \in Ballot] == Def(SA, bb) +<1>2. \A b : SafeAt(b, v) = SA[b] + BY DEF SafeAt +<1>3. ASSUME NEW n \in Nat, NEW g, NEW h, + \A i \in 0..(n-1) : g[i] = h[i] + PROVE Def(g, n) = Def(h, n) + BY <1>3 +<1>4. SA = [b \in Ballot |-> Def(SA, b)] + <2> HIDE DEF Def + <2> QED + BY <1>3, RecursiveFcnOfNat, Isa +<1>5. \A b \in Ballot : SA[b] = Def(SA, b) + <2> HIDE DEF Def + <2> QED + BY <1>4, Zenon +<1>6. QED + BY <1>2, <1>5, Zenon DEF SafeAt +----------------------------------------------------------------------------- + +(***************************************************************************) +(* We now define TypeOK to be the type-correctness invariant. *) +(***************************************************************************) +TypeOK == /\ votes \in [Acceptor -> SUBSET (Ballot \X Value)] + /\ maxBal \in [Acceptor -> Ballot \cup {-1}] + +(***************************************************************************) +(* We now define `chosen' to be the state function so that the algorithm *) +(* specified by formula Spec conjoined with the liveness requirements *) +(* described below implements the algorithm of module Consensus (satisfies *) +(* the specification LiveSpec of that module) under a refinement mapping *) +(* that substitutes this state function `chosen' for the variable `chosen' *) +(* of module Consensus. The definition uses the following one, which *) +(* defines ChosenIn(b, v) to be true iff a quorum of acceptors have all *) +(* voted for v in ballot b. *) +(***************************************************************************) +ChosenIn(b, v) == \E Q \in Quorum : \A a \in Q : VotedFor(a, b, v) + +chosen == {v \in Value : \E b \in Ballot : ChosenIn(b, v)} +----------------------------------------------------------------------------- +(***************************************************************************) +(* The following lemma is used for reasoning about the operator SafeAt. *) +(* It is proved from SafeAtProp by induction. *) +(***************************************************************************) +LEMMA SafeLemma == + TypeOK => + \A b \in Ballot : + \A v \in Value : + SafeAt(b, v) => + \A c \in 0..(b-1) : + \E Q \in Quorum : + \A a \in Q : /\ maxBal[a] >= c + /\ \/ DidNotVoteIn(a, c) + \/ VotedFor(a, c, v) +<1> SUFFICES ASSUME TypeOK + PROVE SafeLemma!2 + OBVIOUS +<1> DEFINE P(b) == \A c \in 0..b : SafeLemma!2!(c) +<1> USE DEF Ballot +<1>1. P(0) + OBVIOUS +<1>2. ASSUME NEW b \in Ballot, P(b) + PROVE P(b+1) + <2>1. /\ b+1 \in Ballot \ {0} + /\ (b+1) - 1 = b + OBVIOUS + <2>2. 0..(b+1) = (0..b) \cup {b+1} + OBVIOUS + <2>3. SUFFICES ASSUME NEW v \in Value, + SafeAt(b+1, v), + NEW c \in 0..b + PROVE \E Q \in Quorum : + \A a \in Q : /\ maxBal[a] >= c + /\ \/ DidNotVoteIn(a, c) + \/ VotedFor(a, c, v) + BY <1>2 + <2>4. PICK Q \in Quorum : + /\ \A a \in Q : maxBal[a] \geq (b+1) + /\ \E cc \in -1..b : + /\ (cc # -1) => /\ SafeAt(cc, v) + /\ \A a \in Q : + \A w \in Value : + VotedFor(a, cc, w) => (w = v) + /\ \A d \in (cc+1)..b, a \in Q : DidNotVoteIn(a, d) + BY SafeAtProp, <2>3, <2>1, Zenon + <2>5. PICK cc \in -1..b : + /\ (cc # -1) => /\ SafeAt(cc, v) + /\ \A a \in Q : + \A w \in Value : + VotedFor(a, cc, w) => (w = v) + /\ \A d \in (cc+1)..b, a \in Q : DidNotVoteIn(a, d) + BY <2>4 + <2>6. CASE c > cc + BY <2>4, <2>5, <2>6, QA DEF TypeOK + <2>7. CASE c = cc + <3>2. \A a \in Q : maxBal[a] \in Ballot \cup {-1} + BY QA DEF TypeOK + <3>3. \A a \in Q : maxBal[a] \geq c + BY <2>4, <2>7, <3>2 + <3>4. \A a \in Q : \/ DidNotVoteIn(a, c) + \/ VotedFor(a, c, v) + BY <2>7, <2>5 DEF DidNotVoteIn + <3>5. QED + BY <3>3, <3>4 + <2>8. CASE c < cc + BY <2>8, <1>2, <2>5 + <2>9. QED + BY <2>6, <2>7, <2>8 +<1>3. \A b \in Ballot : P(b) + BY <1>1, <1>2, NatInduction, Isa +<1>4. QED + BY <1>3 +----------------------------------------------------------------------------- +(***************************************************************************) +(* We now define the invariant that is used to prove the correctness of *) +(* our algorithm--meaning that specification Spec implements specification *) +(* Spec of module Consensus under our refinement mapping. Correctness of *) +(* the voting algorithm follows from the the following three invariants: *) +(* *) +(* VInv1: In any ballot, an acceptor can vote for at most one value. *) +(* *) +(* VInv2: An acceptor can vote for a value v in ballot b iff v is *) +(* safe at b. *) +(* *) +(* VInv3: Two different acceptors cannot vote for different values in *) +(* the same ballot. *) +(* *) +(* Their precise definitions are as follows. *) +(***************************************************************************) +VInv1 == \A a \in Acceptor, b \in Ballot, v, w \in Value : + VotedFor(a, b, v) /\ VotedFor(a, b, w) => (v = w) + +VInv2 == \A a \in Acceptor, b \in Ballot, v \in Value : + VotedFor(a, b, v) => SafeAt(b, v) + +VInv3 == \A a1, a2 \in Acceptor, b \in Ballot, v1, v2 \in Value : + VotedFor(a1, b, v1) /\ VotedFor(a2, b, v2) => (v1 = v2) + +(***************************************************************************) +(* It is obvious, that VInv3 implies VInv1--a fact that we now let TLAPS *) +(* prove as a little check that we haven't made a mistake in our *) +(* definitions. (Actually, we used TLC to check everything before *) +(* attempting any proofs.) We define VInv1 separately because VInv3 is not *) +(* needed for proving safety, only for liveness. *) +(***************************************************************************) +THEOREM VInv3 => VInv1 +BY DEF VInv1, VInv3 +----------------------------------------------------------------------------- +(***************************************************************************) +(* The following lemma proves that SafeAt(b, v) implies that no value *) +(* other than v can have been chosen in any ballot numbered less than b. *) +(* The fact that it also implies that no value other than v can ever be *) +(* chosen in the future follows from this and the fact that SafeAt(b, v) *) +(* is stable--meaning that once it becomes true, it remains true forever. *) +(* The stability of SafeAt(b, v) is proved as step <1>6 of theorem *) +(* InductiveInvariance below. *) +(* *) +(* This lemma is used only in the proof of theorem VT1 below. *) +(***************************************************************************) +LEMMA VT0 == /\ TypeOK + /\ VInv1 + /\ VInv2 + => \A v, w \in Value, b, c \in Ballot : + (b > c) /\ SafeAt(b, v) /\ ChosenIn(c, w) => (v = w) +<1> SUFFICES ASSUME TypeOK, VInv1, VInv2, + NEW v \in Value, NEW w \in Value + PROVE \A b, c \in Ballot : + (b > c) /\ SafeAt(b, v) /\ ChosenIn(c, w) => (v = w) + OBVIOUS +<1> P(b) == \A c \in Ballot : + (b > c) /\ SafeAt(b, v) /\ ChosenIn(c, w) => (v = w) +<1> USE DEF Ballot + +<1>1. P(0) + OBVIOUS +<1>2. ASSUME NEW b \in Ballot, \A i \in 0..(b-1) : P(i) + PROVE P(b) + <2>1. CASE b = 0 + BY <2>1 + <2>2. CASE b # 0 + <3>1. SUFFICES ASSUME NEW c \in Ballot, b > c, SafeAt(b, v), ChosenIn(c, w) + PROVE v=w + OBVIOUS + <3>2. PICK Q \in Quorum : \A a \in Q : VotedFor(a, c, w) + BY <3>1 DEF ChosenIn + <3>3. PICK QQ \in Quorum, + d \in -1..(b-1) : + /\ (d # -1) => /\ SafeAt(d, v) + /\ \A a \in QQ : + \A x \in Value : + VotedFor(a, d, x) => (x = v) + /\ \A e \in (d+1)..(b-1), a \in QQ : DidNotVoteIn(a, e) + BY <2>2, <3>1, SafeAtProp, Zenon + <3> PICK aa \in QQ \cap Q : TRUE + BY QA + <3>4. c \leq d + BY <3>1, <3>2, <3>3 DEF DidNotVoteIn + <3>5. CASE c = d + BY <3>2, <3>3, <3>4, <3>5 + <3>6. CASE d > c + BY <1>2, <3>1, <3>3, <3>4, <3>6 + <3>7. QED + BY <3>4, <3>5, <3>6 + <2>. QED BY <2>1, <2>2 +<1>3. \A b \in Ballot : P(b) + <2>. HIDE DEF P + <2>. QED BY <1>2, GeneralNatInduction, Isa +<1>4. QED + BY <1>3 + +(***************************************************************************) +(* The following theorem asserts that the invariance of TypeOK, VInv1, and *) +(* VInv2 implies that the algorithm satisfies the basic consensus property *) +(* that at most one value is chosen (at any time). If you can prove it, *) +(* then you understand why the Paxos consensus algorithm allows only a *) +(* single value to be chosen. Note that VInv3 is not needed to prove this *) +(* property. *) +(***************************************************************************) +THEOREM VT1 == /\ TypeOK + /\ VInv1 + /\ VInv2 + => \A v, w : + (v \in chosen) /\ (w \in chosen) => (v = w) +<1>1. SUFFICES ASSUME TypeOK, VInv1, VInv2, + NEW v, NEW w, + v \in chosen, w \in chosen + PROVE v = w + OBVIOUS +<1>2. v \in Value /\ w \in Value + BY <1>1 DEF chosen +<1>3. PICK b \in Ballot, c \in Ballot : ChosenIn(b, v) /\ ChosenIn(c, w) + BY <1>1 DEF chosen +<1>4. PICK Q \in Quorum, R \in Quorum : + /\ \A a \in Q : VotedFor(a, b, v) + /\ \A a \in R : VotedFor(a, c, w) + BY <1>3 DEF ChosenIn +<1>5. PICK av \in Q, aw \in R: /\ VotedFor(av, b, v) + /\ VotedFor(aw, c, w) + BY <1>4, QuorumNonEmpty +<1>6. SafeAt(b, v) /\ SafeAt(c, w) + BY <1>1, <1>2, <1>5, QA DEF VInv2 +<1>7. CASE b = c + <2> PICK a \in Q \cap R : TRUE + BY QA + <2>1. /\ VotedFor(a, b, v) + /\ VotedFor(a, c, w) + BY <1>4 + <2>2. QED + BY <1>1, <1>2, <1>7, <2>1, QA DEF VInv1 +<1>8. CASE b > c + BY <1>1, <1>6, <1>3, <1>8, VT0, <1>2 +<1>9. CASE c > b + BY <1>1, <1>6, <1>3, <1>9, VT0, <1>2 +<1>10. QED + BY <1>7, <1>8, <1>9 DEF Ballot + +(***************************************************************************) +(* The rest of the proof uses only the primed version of VT1--that is, the *) +(* theorem whose statement is VT1'. (Remember that VT1 names the formula *) +(* being asserted by the theorem we call VT1.) The formula VT1' asserts *) +(* that VT1 is true in the second state of any transition (pair of *) +(* states). We derive that theorem from VT1 by simple temporal logic, and *) +(* similarly for VT0 and SafeAtProp. *) +(***************************************************************************) +THEOREM SafeAtPropPrime == + \A b \in Ballot, v \in Value : + SafeAt(b, v)' <=> + \/ b = 0 + \/ \E Q \in Quorum : + /\ \A a \in Q : maxBal'[a] \geq b + /\ \E c \in -1..(b-1) : + /\ (c # -1) => /\ SafeAt(c, v)' + /\ \A a \in Q : + \A w \in Value : + VotedFor(a, c, w)' => (w = v) + /\ \A d \in (c+1)..(b-1), a \in Q : DidNotVoteIn(a, d)' +<1>1. SafeAtProp' BY SafeAtProp, PTL +<1>. QED BY <1>1 + +LEMMA VT0Prime == + /\ TypeOK' + /\ VInv1' + /\ VInv2' + => \A v, w \in Value, b, c \in Ballot : + (b > c) /\ SafeAt(b, v)' /\ ChosenIn(c, w)' => (v = w) +<1>1. VT0' BY VT0, PTL +<1>. QED BY <1>1 + +THEOREM VT1Prime == + /\ TypeOK' + /\ VInv1' + /\ VInv2' + => \A v, w : + (v \in chosen') /\ (w \in chosen') => (v = w) +<1>1. VT1' BY VT1, PTL +<1>. QED BY <1>1 + +----------------------------------------------------------------------------- +(***************************************************************************) +(* The invariance of VInv2 depends on SafeAt(b, v) being stable, meaning *) +(* that once it becomes true it remains true forever. Stability of *) +(* SafeAt(b, v) depends on the following invariant. *) +(***************************************************************************) +VInv4 == \A a \in Acceptor, b \in Ballot : + maxBal[a] < b => DidNotVoteIn(a, b) + +(***************************************************************************) +(* The inductive invariant that we use to prove correctness of this *) +(* algorithm is VInv, defined as follows. *) +(***************************************************************************) +VInv == TypeOK /\ VInv2 /\ VInv3 /\ VInv4 +----------------------------------------------------------------------------- +(***************************************************************************) +(* To simplify reasoning about the next-state action Next, we want to *) +(* express it in a more convenient form. This is done by lemma NextDef *) +(* below, which shows that Next equals an action defined in terms of the *) +(* following subactions. *) +(***************************************************************************) +IncreaseMaxBal(self, b) == + /\ b > maxBal[self] + /\ maxBal' = [maxBal EXCEPT ![self] = b] + /\ UNCHANGED votes + +VoteFor(self, b, v) == + /\ maxBal[self] \leq b + /\ DidNotVoteIn(self, b) + /\ \A p \in Acceptor \ {self} : + \A w \in Value : VotedFor(p, b, w) => (w = v) + /\ SafeAt(b, v) + /\ votes' = [votes EXCEPT ![self] = votes[self] \cup {<>}] + /\ maxBal' = [maxBal EXCEPT ![self] = b] + +BallotAction(self, b) == + \/ IncreaseMaxBal(self, b) + \/ \E v \in Value : VoteFor(self, b, v) + +(***************************************************************************) +(* When proving lemma NextDef, we were surprised to discover that it *) +(* required the assumption that the set of acceptors is non-empty. This *) +(* assumption isn't necessary for safety, since if there are no acceptors *) +(* there can be no quorums (see theorem QuorumNonEmpty above) so no value *) +(* is ever chosen and the Consensus specification is trivially implemented *) +(* under our refinement mapping. However, the assumption is necessary for *) +(* liveness and it allows us to lemma NextDef for the safety proof as *) +(* well, so we assert it now. *) +(***************************************************************************) +ASSUME AcceptorNonempty == Acceptor # {} + +(***************************************************************************) +(* The proof of the lemma itself is quite simple. *) +(***************************************************************************) +LEMMA NextDef == + TypeOK => + (Next = \E self \in Acceptor : + \E b \in Ballot : BallotAction(self, b) ) +<1> HAVE TypeOK +<1>2. Next = \E self \in Acceptor: acceptor(self) + BY AcceptorNonempty DEF Next, ProcSet +<1>3. @ = NextDef!2!2 + BY DEF Next, BallotAction, IncreaseMaxBal, VoteFor, ProcSet, acceptor +<1>4. QED + BY <1>2, <1>3 +----------------------------------------------------------------------------- +(***************************************************************************) +(* We now come to the proof that VInv is an invariant of the *) +(* specification. This follows from the following result, which asserts *) +(* that it is an inductive invariant of the next-state action. This fact *) +(* is used in the liveness proof as well. *) +(***************************************************************************) +THEOREM InductiveInvariance == VInv /\ [Next]_vars => VInv' +<1>1. VInv /\ (vars' = vars) => VInv' + BY Isa + DEF VInv, vars, TypeOK, VInv2, VotedFor, SafeAt, DidNotVoteIn, VInv3, VInv4 +<1> SUFFICES ASSUME VInv, + NEW self \in Acceptor, + NEW b \in Ballot, + BallotAction(self, b) + PROVE VInv' + BY <1>1, NextDef DEF VInv + +<1>2. TypeOK' + <2>1. CASE IncreaseMaxBal(self, b) + BY <2>1 DEF IncreaseMaxBal, VInv, TypeOK + <2>2. CASE \E v \in Value : VoteFor(self, b, v) + BY <2>2 DEF VInv, TypeOK, VoteFor + <2>3. QED + BY <2>1, <2>2 DEF BallotAction + +<1>3. ASSUME NEW a \in Acceptor, NEW c \in Ballot, NEW w \in Value, + VotedFor(a, c, w) + PROVE VotedFor(a, c, w)' + <2>1. CASE IncreaseMaxBal(self, b) + BY <2>1, <1>3 DEF IncreaseMaxBal, VotedFor + <2>2. CASE \E v \in Value : VoteFor(self, b, v) + <3>1. PICK v \in Value : VoteFor(self, b, v) + BY <2>2 + <3>2. CASE a = self + <4>1. votes'[a] = votes[a] \cup {<>} + BY <3>1, <3>2 DEF VoteFor, VInv, TypeOK + <4>2. QED + BY <1>3, <4>1 DEF VotedFor + <3>3. CASE a # self + <4>1. votes[a] = votes'[a] + BY <3>1, <3>3 DEF VoteFor, VInv, TypeOK + <4>2. QED + BY <1>3, <4>1 DEF VotedFor + <3>4. QED + BY <3>2, <3>3 DEF VoteFor + <2>3. QED + BY <2>1, <2>2 DEF BallotAction + +<1>4. ASSUME NEW a \in Acceptor, NEW c \in Ballot, NEW w \in Value, + ~VotedFor(a, c, w), VotedFor(a, c, w)' + PROVE (a = self) /\ (c = b) /\ VoteFor(self, b, w) + <2>1. CASE IncreaseMaxBal(self, b) + BY <2>1, <1>4 DEF IncreaseMaxBal, VInv, TypeOK, VotedFor + <2>2. CASE \E v \in Value : VoteFor(self, b, v) + <3>1. PICK v \in Value : VoteFor(self, b, v) + BY <2>2 + <3>2. a = self + BY <3>1, <1>4 DEF VoteFor, VInv, TypeOK, VotedFor + <3>3. votes'[a] = votes[a] \cup {<>} + BY <3>1, <3>2 DEF VoteFor, VInv, TypeOK + <3>4. c = b /\ v = w + BY <1>4, <3>3 DEF VotedFor + <3>5. QED + BY <3>1, <3>2, <3>4 + <2>3. QED + BY <2>1, <2>2 DEF BallotAction + +<1>5. ASSUME NEW a \in Acceptor + PROVE /\ maxBal[a] \in Ballot \cup {-1} + /\ maxBal'[a] \in Ballot \cup {-1} + /\ maxBal'[a] >= maxBal[a] + BY DEF VInv, TypeOK, IncreaseMaxBal, VInv, VoteFor, BallotAction, DidNotVoteIn, + VotedFor, Ballot + +<1>6. ASSUME NEW c \in Ballot, NEW w \in Value, + SafeAt(c, w) + PROVE SafeAt(c, w)' + <2> USE DEF Ballot + <2> DEFINE P(i) == \A j \in 0..i : SafeAt(j, w) => SafeAt(j, w)' + <2>1. P(0) + BY SafeAtPropPrime, 0 .. 0 = {0}, Zenon + <2>2. ASSUME NEW d \in Ballot, P(d) + PROVE P(d+1) + <3>1. SUFFICES ASSUME NEW e \in 0..(d+1), SafeAt(e, w) + PROVE SafeAt(e, w)' + OBVIOUS + <3>2. CASE e \in 0..d + BY <2>2, <3>1, <3>2 + <3>3. CASE e = d+1 + <4>. e \in Ballot \ {0} + BY <3>3 + <4>1. PICK Q \in Quorum : SafeAtProp!(e, w)!2!2!(Q) + BY <3>1, SafeAtProp, Zenon + <4>2. \A aa \in Q : maxBal'[aa] \geq e + BY <1>5, <4>1, QA + <4>3. \E cc \in -1..(e-1) : + /\ (cc # -1) => /\ SafeAt(cc, w)' + /\ \A ax \in Q : + \A z \in Value : + VotedFor(ax, cc, z)' => (z = w) + /\ \A dd \in (cc+1)..(e-1), ax \in Q : DidNotVoteIn(ax, dd)' + <5>1. ASSUME NEW cc \in 0..(e-1), + NEW ax \in Q, NEW z \in Value, + VotedFor(ax, cc, z)', ~ VotedFor(ax, cc, z) + PROVE FALSE + <6>1. (ax = self) /\ (cc = b) /\ VoteFor(self, b, z) + BY <5>1, <1>4, QA + <6>2. /\ maxBal[ax] \geq e + /\ maxBal[self] \leq b + BY <4>1, <6>1 DEF VoteFor + <6>. QED BY <3>3, <6>1, <6>2 DEF VInv, TypeOK + <5>2. PICK cc \in -1..(e-1) : SafeAtProp!(e, w)!2!2!(Q)!2!(cc) + BY <4>1 + <5>3. ASSUME cc # -1 + PROVE /\ SafeAt(cc, w)' + /\ \A ax \in Q : \A z \in Value : + VotedFor(ax, cc, z)' => (z = w) + <6>1. /\ SafeAt(cc, w) + /\ \A ax \in Q : + \A z \in Value : VotedFor(ax, cc, z) => (z = w) + BY <5>2, <5>3 + <6>2. SafeAt(cc, w)' + BY <6>1, <5>3, <3>3, <2>2 + <6>3. ASSUME NEW ax \in Q, NEW z \in Value, VotedFor(ax, cc, z)' + PROVE z = w + <7>1. CASE VotedFor(ax, cc, z) + BY <6>1, <7>1 + <7>2. CASE ~ VotedFor(ax, cc, z) + BY <7>2, <6>3, <5>1, <5>3 + <7>3. QED + BY <7>1, <7>2 + <6>4. QED + BY <6>2, <6>3 + <5>4. ASSUME NEW dd \in (cc+1)..(e-1), NEW ax \in Q, + ~ DidNotVoteIn(ax, dd)' + PROVE FALSE + BY <5>2, <5>1, <5>4 DEF DidNotVoteIn + <5>5. QED + BY <5>3, <5>4 + <4>4. \/ e = 0 + \/ \E Q_1 \in Quorum : + /\ \A aa \in Q_1 : maxBal'[aa] \geq e + /\ \E c_1 \in -1..e - 1 : + /\ c_1 # -1 + => (/\ SafeAt(c_1, w)' + /\ \A aa \in Q_1 : + \A w_1 \in Value : + VotedFor(aa, c_1, w_1)' => w_1 = w) + /\ \A d_1 \in c_1 + 1..e - 1, aa \in Q_1 : + DidNotVoteIn(aa, d_1)' + BY <4>2, <4>3, <3>3 + <4>6. SafeAt(e, w)' <=> <4>4 + BY SafeAtPropPrime, <3>3, Zenon + <4>7. QED + BY <4>2, <4>3, <4>6 + <3>4. QED + BY <3>2, <3>3 + <2>3. \A d \in Ballot : P(d) + BY <2>1, <2>2, NatInduction, Isa + <2>4. QED + BY <2>3, <1>6 + +<1>7. VInv2' + <2>1. SUFFICES ASSUME NEW a \in Acceptor, NEW c \in Ballot, NEW v \in Value, + VotedFor(a, c, v)' + PROVE SafeAt(c, v)' + BY DEF VInv2 + <2>2. CASE VotedFor(a, c, v) + BY <1>6, <2>2 DEF VInv, VInv2 + <2>3. CASE ~VotedFor(a, c, v) + BY <1>6, <2>1, <2>3, <1>4 DEF VoteFor + <2>4. QED + BY <2>2, <2>3 + +<1>8. VInv3' + <2>1. ASSUME NEW a1 \in Acceptor, NEW a2\in Acceptor, + NEW c \in Ballot, NEW v1 \in Value, NEW v2 \in Value, + VotedFor(a1, c, v1)', + VotedFor(a2, c, v2)', + VotedFor(a1, c, v1), + VotedFor(a2, c, v2) + PROVE v1 = v2 + BY <2>1 DEF VInv, VInv3 + <2>2. ASSUME NEW a1 \in Acceptor, NEW a2\in Acceptor, + NEW c \in Ballot, NEW v1 \in Value, NEW v2 \in Value, + VotedFor(a1, c, v1)', + VotedFor(a2, c, v2)', + ~ VotedFor(a1, c, v1) + PROVE v1 = v2 + <3>1. (a1 = self) /\ (c = b) /\ VoteFor(self, b, v1) + BY <2>2, <1>4 + <3>2. CASE a2 = self + <4>1. ~VotedFor(self, b, v2) + BY <3>1 DEF VoteFor, DidNotVoteIn + <4>2. VoteFor(self, b, v2) + BY <2>2, <3>1, <3>2, <4>1, <1>4 + <4>. QED BY <3>1, <4>2, <2>2 DEF VotedFor, VoteFor, VInv, TypeOK + <3>3. CASE a2 # self + BY <3>1, <3>3, <2>2 DEF VotedFor, VoteFor, VInv, TypeOK + <3>4. QED + BY <3>2, <3>3 + <2>3. QED + BY <2>1, <2>2 DEF VInv3 + +<1>9. VInv4' + <2>1. SUFFICES ASSUME NEW a \in Acceptor, NEW c \in Ballot, + maxBal'[a] < c, + ~ DidNotVoteIn(a, c)' + PROVE FALSE + BY DEF VInv4 + <2>2. maxBal[a] < c + BY <1>5, <2>1 DEF Ballot + <2>3. DidNotVoteIn(a, c) + BY <2>2 DEF VInv, VInv4 + <2>4. PICK v \in Value : VotedFor(a, c, v)' + BY <2>1 DEF DidNotVoteIn + <2>5. (a = self) /\ (c = b) /\ VoteFor(self, b, v) + BY <1>4, <2>1, <2>3, <2>4 DEF DidNotVoteIn + <2>6. maxBal'[a] = c + BY <2>5 DEF VoteFor, VInv, TypeOK + <2>7. QED + BY <2>1, <2>6 DEF Ballot + +<1>10. QED + BY <1>2, <1>7, <1>8, <1>9 DEF VInv + +(***************************************************************************) +(* The invariance of VInv follows easily from theorem InductiveInvariance *) +(* and the following result, which is easy to prove with TLAPS. *) +(***************************************************************************) +THEOREM InitImpliesInv == Init => VInv +BY DEF Init, VInv, TypeOK, ProcSet, VInv2, VInv3, VInv4, VotedFor, DidNotVoteIn + +(***************************************************************************) +(* The following theorem asserts that VInv is an invariant of Spec. *) +(***************************************************************************) +THEOREM VT2 == Spec => []VInv +BY InitImpliesInv, InductiveInvariance, PTL DEF Spec + +----------------------------------------------------------------------------- +(***************************************************************************) +(* The following INSTANCE statement instantiates module Consensus with the *) +(* following expressions substituted for the parameters (the CONSTANTS and *) +(* VARIABLES) of that module: *) +(* *) +(* Parameter of Consensus Expression (of this module) *) +(* ---------------------- --------------------------- *) +(* Value Value *) +(* chosen chosen *) +(* *) +(* (Note that if no substitution is specified for a parameter, the default *) +(* is to substitute the parameter or defined operator of the same name.) *) +(* More precisely, for each defined identifier id of module Consensus, *) +(* this statement defines C!id to equal the value of id under these *) +(* substitutions. *) +(***************************************************************************) +C == INSTANCE Consensus +Refines == C!Spec + +(***************************************************************************) +(* The following theorem asserts that the safety properties of the voting *) +(* algorithm (specified by formula Spec) of this module implement the *) +(* consensus safety specification Spec of module Consensus under the *) +(* substitution (refinement mapping) of the INSTANCE statement. *) +(***************************************************************************) +THEOREM VT3 == Spec => C!Spec +<1>1. Init => C!Init + <2> SUFFICES ASSUME Init + PROVE C!Init + OBVIOUS + <2>1. SUFFICES ASSUME NEW v \in chosen + PROVE FALSE + BY DEF C!Init + <2>2. PICK b \in Ballot, Q \in Quorum : \A a \in Q : VotedFor(a, b, v) + BY <2>1 DEF chosen, ChosenIn + <2>3. PICK a \in Q : <> \in votes[a] + BY QuorumNonEmpty, <2>2 DEF VotedFor + <2>4. QED + BY <2>3, QA DEF Init + +<1>2. VInv /\ VInv'/\ [Next]_vars => [C!Next]_C!vars + <2>. SUFFICES ASSUME VInv, VInv', [Next]_vars + PROVE [C!Next]_C!vars + OBVIOUS + <2>1. CASE vars' = vars + BY <2>1 DEF vars, C!vars, chosen, ChosenIn, VotedFor + <2>2. SUFFICES ASSUME NEW self \in Acceptor, + NEW b \in Ballot, + BallotAction(self, b) + PROVE [C!Next]_C!vars + BY <2>1, NextDef DEF VInv + <2>3. ASSUME IncreaseMaxBal(self, b) + PROVE C!vars' = C!vars + BY <2>3 DEF IncreaseMaxBal, C!vars, chosen, ChosenIn, VotedFor + <2>4. ASSUME NEW v \in Value, + VoteFor(self, b, v) + PROVE [C!Next]_C!vars + <3>3. ASSUME NEW w \in chosen + PROVE w \in chosen' + <4>1. PICK c \in Ballot, Q \in Quorum : \A a \in Q : <> \in votes[a] + BY <3>3 DEF chosen, ChosenIn, VotedFor + <4>2. SUFFICES ASSUME NEW a \in Q + PROVE <> \in votes'[a] + BY DEF chosen, ChosenIn, VotedFor + <4>3. CASE a = self + BY <2>4, <4>1, <4>3 DEF VoteFor, VInv, TypeOK + <4>4. CASE a # self + BY <2>4, <4>1, <4>4, QA DEF VoteFor, VInv, TypeOK + <4>5. QED + BY <4>3, <4>4 + <3>1. ASSUME NEW w \in chosen, + v \in chosen' + PROVE w = v + BY <3>3, <3>1, VT1Prime DEF VInv, VInv1, VInv3 + <3>2. ASSUME NEW w, w \notin chosen, w \in chosen' + PROVE w = v + <4>2. PICK c \in Ballot, Q \in Quorum : \A a \in Q : <> \in votes'[a] + BY <3>2 DEF chosen, ChosenIn, VotedFor + <4>3. PICK a \in Q : <> \notin votes[a] + BY <3>2 DEF chosen, ChosenIn, VotedFor + <4>4. CASE a = self + BY <2>4, <4>4, <4>2, <4>3 DEF VoteFor, VInv, TypeOK + <4>5. CASE a # self + BY <2>4, <4>2, <4>3, <4>5, QA DEF VoteFor, VInv, TypeOK + <4>6. QED + BY <4>4, <4>5 + <3>. QED + BY <3>3, <3>1, <3>2 DEF C!Next, C!vars + <2>5. QED + BY <2>2, <2>3, <2>4 DEF BallotAction +<1>3. QED + BY <1>1, <1>2, VT2, PTL DEF Spec, C!Spec + +----------------------------------------------------------------------------- +(***************************************************************************) +(* Liveness *) +(* *) +(* We now state the liveness property required of our voting algorithm and *) +(* prove that it and the safety property imply specification LiveSpec of *) +(* module Consensus under our refinement mapping. *) +(* *) +(* We begin by stating two additional assumptions that are necessary for *) +(* liveness. Liveness requires that some value eventually be chosen. *) +(* This cannot hold with an infinite set of acceptors. More precisely, *) +(* liveness requires the existence of a finite quorum. (Otherwise, it *) +(* would be impossible for all acceptors of any quorum ever to have voted, *) +(* so no value could ever be chosen.) Moreover, it is impossible to choose *) +(* a value if there are no values. Hence, we make the following two *) +(* assumptions. *) +(***************************************************************************) +ASSUME AcceptorFinite == IsFiniteSet(Acceptor) + +ASSUME ValueNonempty == Value # {} +----------------------------------------------------------------------------- + +LEMMA FiniteSetHasMax == + ASSUME NEW S \in SUBSET Int, IsFiniteSet(S), S # {} + PROVE \E max \in S : \A x \in S : max >= x +<1>. DEFINE P(T) == T \in SUBSET Int /\ T # {} => \E max \in T : \A x \in T : max >= x +<1>1. P({}) + OBVIOUS +<1>2. ASSUME NEW T, NEW x, P(T), x \notin T + PROVE P(T \cup {x}) + BY <1>2 +<1>3. \A T : IsFiniteSet(T) => P(T) + <2>. HIDE DEF P + <2>. QED BY <1>1, <1>2, FS_Induction, IsaM("blast") +<1>. QED BY <1>3, Zenon + +----------------------------------------------------------------------------- +(***************************************************************************) +(* The following theorem implies that it is always possible to find a *) +(* ballot number b and a value v safe at b by choosing b large enough and *) +(* then having a quorum of acceptors perform IncreaseMaxBal(b) actions. *) +(* It will be used in the liveness proof. Observe that it is for *) +(* liveness, not safety, that invariant VInv3 is required. *) +(***************************************************************************) +THEOREM VT4 == TypeOK /\ VInv2 /\ VInv3 => + \A Q \in Quorum, b \in Ballot : + (\A a \in Q : (maxBal[a] >= b)) => \E v \in Value : SafeAt(b,v) +\* Checked as an invariant by TLC with 3 acceptors, 3 ballots, 2 values +<1>. USE DEF Ballot +<1>1. SUFFICES ASSUME TypeOK, VInv2, VInv3, + NEW Q \in Quorum, NEW b \in Ballot, + (\A a \in Q : (maxBal[a] >= b)) + PROVE \E v \in Value : SafeAt(b, v) + OBVIOUS +<1>2. CASE b = 0 + BY ValueNonempty, <1>1, SafeAtProp, <1>2, Zenon +<1>4. SUFFICES ASSUME b # 0 + PROVE \E v \in Value : + \E c \in -1..(b-1) : + /\ (c # -1) => /\ SafeAt(c, v) + /\ \A a \in Q : + \A w \in Value : + VotedFor(a, c, w) => (w = v) + /\ \A d \in (c+1)..(b-1), a \in Q : DidNotVoteIn(a, d) + BY <1>1, <1>2, SafeAtProp +<1>5. CASE \A a \in Q, c \in 0..(b-1) : DidNotVoteIn(a, c) + BY <1>5, ValueNonempty +<1>6. CASE \E a \in Q, c \in 0..(b-1) : ~DidNotVoteIn(a, c) + <2>1. PICK c \in 0..(b-1) : + /\ \E a \in Q : ~DidNotVoteIn(a, c) + /\ \A d \in (c+1)..(b-1), a \in Q : DidNotVoteIn(a, d) + <3> DEFINE S == {c \in 0..(b-1) : \E a \in Q : ~DidNotVoteIn(a, c)} + <3>1. S # {} + BY <1>6 + <3>2. PICK c \in S : \A d \in S : c >= d + <4>2. IsFiniteSet(S) + BY FS_Interval, FS_Subset, 0 \in Int, b-1 \in Int, Zenon + <4>3. QED + BY <3>1, <4>2, FiniteSetHasMax + <3>. QED + BY <3>2 DEF Ballot + <2>4. PICK a0 \in Q, v \in Value : VotedFor(a0, c, v) + BY <2>1 DEF DidNotVoteIn + <2>5. \A a \in Q : \A w \in Value : + VotedFor(a, c, w) => (w = v) + BY <2>4, QA, <1>1 DEF VInv3 + <2>6. SafeAt(c, v) + BY <1>1, <2>4, QA DEF VInv2 + <2>7. QED + BY <2>1, <2>5, <2>6 +<1>7. QED + BY <1>5, <1>6 +------------------------------------------------------------------------------- +(***************************************************************************) +(* The progress property we require of the algorithm is that a quorum of *) +(* acceptors, by themselves, can eventually choose a value v. This means *) +(* that, for some quorum Q and ballot b, the acceptors `a' of Q must make *) +(* SafeAt(b, v) true by executing IncreaseMaxBal(a, b) and then must *) +(* execute VoteFor(a, b, v) to choose v. In order to be able to execute *) +(* VoteFor(a, b, v), acceptor `a' must not execute a Ballot(a, c) action *) +(* for any c > b. *) +(* *) +(* These considerations lead to the following liveness requirement *) +(* LiveAssumption. The WF condition ensures that the acceptors `a' in Q *) +(* eventually execute the necessary BallotAction(a, b) actions if they are *) +(* enabled, and the [][...]_vars condition ensures that they never perform *) +(* BallotAction actions for higher-numbered ballots, so the necessary *) +(* BallotAction(a, b) actions are enabled. *) +(***************************************************************************) +LiveAssumption == + \E Q \in Quorum, b \in Ballot : + /\ \A self \in Q : WF_vars(BallotAction(self, b)) + /\ [] [\A self \in Q : \A c \in Ballot : + (c > b) => ~ BallotAction(self, c)]_vars + +LiveSpec == Spec /\ LiveAssumption +(***************************************************************************) +(* LiveAssumption is stronger than necessary. Instead of requiring that *) +(* an acceptor in Q never executes an action of a higher-numbered ballot *) +(* than b, it suffices that it doesn't execute such an action until unless *) +(* it has voted in ballot b. However, the natural liveness requirement *) +(* for a Paxos consensus algorithm implies condition LiveAssumption. *) +(* *) +(* Condition LiveAssumption is a liveness property, constraining only what *) +(* eventually happens. It is straightforward to replace "eventually *) +(* happens" by "happens within some length of time" and convert *) +(* LiveAssumption into a real-time condition. We have not done that for *) +(* three reasons: *) +(* *) +(* 1. The real-time requirement and, we believe, the real-time reasoning *) +(* will be more complicated, since temporal logic was developed to *) +(* abstract away much of the complexity of reasoning about explicit *) +(* times. *) +(* *) +(* 2. TLAPS does not yet support reasoning about real numbers. *) +(* *) +(* 3. Reasoning about real-time specifications consists entirely *) +(* of safety reasoning, which is almost entirely action reasoning. *) +(* We want to see how the TLA+ proof language and TLAPS do on *) +(* temporal logic reasoning. *) +(* *) +(* *) +(***************************************************************************) +----------------------------------------------------------------------------- +(***************************************************************************) +(* Here are two temporal-logic proof rules. Their validity is obvious *) +(* when you understand what they mean. *) +(***************************************************************************) +THEOREM AlwaysForall == + ASSUME NEW CONSTANT S, NEW TEMPORAL P(_) + PROVE (\A s \in S : []P(s)) <=> [](\A s \in S : P(s)) +OBVIOUS + +LEMMA EventuallyAlwaysForall == + ASSUME NEW CONSTANT S, IsFiniteSet(S), + NEW TEMPORAL P(_) + PROVE (\A s \in S : <>[]P(s)) => <>[](\A s \in S : P(s)) +<1>. DEFINE A(x) == <>[]P(x) + L(T) == \A s \in T : A(s) + R(T) == \A s \in T : P(s) + Q(T) == L(T) => <>[]R(T) +<1>1. Q({}) + <2>1. R({}) OBVIOUS + <2>2. <>[]R({}) BY <2>1, PTL + <2>. QED BY <2>2 +<1>2. ASSUME NEW T, NEW x + PROVE Q(T) => Q(T \cup {x}) + <2>1. L(T \cup {x}) => A(x) + <3>. HIDE DEF A + <3>. QED OBVIOUS + <2>2. L(T \cup {x}) /\ Q(T) => <>[]R(T) + OBVIOUS + <2>3. <>[]R(T) /\ A(x) => <>[](R(T) /\ P(x)) + BY PTL + <2>4. R(T) /\ P(x) => R(T \cup {x}) + OBVIOUS + <2>5. <>[](R(T) /\ P(x)) => <>[]R(T \cup {x}) + BY <2>4, PTL + <2>. QED + BY <2>1, <2>2, <2>3, <2>5 +<1>. HIDE DEF Q +<1>3. \A T : IsFiniteSet(T) => Q(T) + BY <1>1, <1>2, FS_Induction, IsaM("blast") +<1>4. Q(S) + BY <1>3 +<1>. QED + BY <1>4 DEF Q +----------------------------------------------------------------------------- +(***************************************************************************) +(* Here is our proof that LiveSpec implements the specification LiveSpec *) +(* of module Consensus under our refinement mapping. *) +(***************************************************************************) +THEOREM Liveness == LiveSpec => C!LiveSpec +<1> SUFFICES ASSUME NEW Q \in Quorum, NEW b \in Ballot + PROVE Spec /\ LiveAssumption!(Q, b) => C!LiveSpec + BY Isa DEF LiveSpec, LiveAssumption + +<1>a. IsFiniteSet(Q) + BY QA, AcceptorFinite, FS_Subset + +<1>1. C!LiveSpec <=> C!Spec /\ ([]<><>_C!vars \/ []<>(chosen # {})) + BY ValueNonempty, C!LiveSpecEquals + +<1> DEFINE LNext == \E self \in Acceptor, c \in Ballot : + /\ BallotAction(self, c) + /\ (self \in Q) => (c =< b) + +<1>2. Spec /\ LiveAssumption!(Q, b) => [][LNext]_vars + <2>1. /\ TypeOK + /\ [Next]_vars + /\ [\A self \in Q : \A c \in Ballot : (c > b) => ~ BallotAction(self, c)]_vars + => [LNext]_vars + BY NextDef DEF LNext, Ballot + <2>2. /\ []TypeOK + /\ [][Next]_vars + /\ [][\A self \in Q : \A c \in Ballot : (c > b) => ~ BallotAction(self, c)]_vars + => [][LNext]_vars + BY <2>1, PTL + <2>3. QED + BY <2>2, VT2, Isa DEF Spec, VInv + +<1> DEFINE LNInv1 == \A a \in Q : maxBal[a] =< b + LInv1 == VInv /\ LNInv1 + +<1>3. LInv1 /\ [LNext]_vars => LInv1' + <2>1. SUFFICES ASSUME LInv1, [LNext]_vars + PROVE LInv1' + OBVIOUS + <2>2. VInv' + BY <2>1, NextDef, InductiveInvariance DEF LInv1, VInv + <2>3. LNInv1' + BY <2>1, QA DEF BallotAction, IncreaseMaxBal, VoteFor, VInv, TypeOK, vars + <2>. QED + BY <2>2, <2>3 + +<1>4. \A a \in Q : + VInv /\ (maxBal[a] = b) /\ [LNext]_vars => VInv' /\ (maxBal'[a] = b) + <2>1. SUFFICES ASSUME NEW a \in Q, + VInv, maxBal[a] = b, [LNext]_vars + PROVE VInv' /\ (maxBal'[a] = b) + OBVIOUS + <2>2. VInv' + BY <2>1, NextDef, InductiveInvariance DEF VInv + <2>3. maxBal'[a] = b + BY <2>1, QA DEF BallotAction, IncreaseMaxBal, VoteFor, VInv, TypeOK, Ballot, vars + <2>. QED + BY <2>2, <2>3 + +<1>5. Spec /\ LiveAssumption!(Q, b) => + <>[](\A self \in Q : maxBal[self] = b) + <2>1. SUFFICES ASSUME NEW self \in Q + PROVE Spec /\ LiveAssumption!(Q, b) => <>[](maxBal[self] = b) +\* BY <1>a, EventuallyAlwaysForall \* doesn't check, even when introducing definitions + PROOF OMITTED + <2> DEFINE P == LInv1 /\ ~(maxBal[self] = b) + QQ == LInv1 /\ (maxBal[self] = b) + A == BallotAction(self, b) + <2>2. [][LNext]_vars /\ WF_vars(A) => (LInv1 ~> QQ) + <3>1. P /\ [LNext]_vars => (P' \/ QQ') + BY <1>3 + <3>2. P /\ <>_vars => QQ' + <4>1. SUFFICES ASSUME LInv1, LNext, A + PROVE QQ' + OBVIOUS + <4>2. LInv1' + BY <4>1, <1>3 + <4>3. CASE IncreaseMaxBal(self, b) + BY <4>1, <4>2, <4>3, QA DEF IncreaseMaxBal, VInv, TypeOK + <4>4. CASE \E v \in Value : VoteFor(self, b, v) + BY <4>1, <4>2, <4>4, QA DEF VoteFor, VInv, TypeOK + <4>5. QED + BY <4>1, <4>3, <4>4 DEF BallotAction + <3>3. P => ENABLED <>_vars + <4>1. (ENABLED <>_vars) <=> + \E votesp, maxBalp: + /\ \/ /\ b > maxBal[self] + /\ maxBalp = [maxBal EXCEPT ![self] = b] + /\ votesp = votes + \/ \E v \in Value : + /\ maxBal[self] \leq b + /\ DidNotVoteIn(self, b) + /\ \A p \in Acceptor \ {self} : + \A w \in Value : VotedFor(p, b, w) => (w = v) + /\ SafeAt(b, v) + /\ votesp = [votes EXCEPT ![self] = votes[self] + \cup {<>}] + /\ maxBalp = [maxBal EXCEPT ![self] = b] + /\ <> # <> +\* BY DEF BallotAction, IncreaseMaxBal, VoteFor, vars, SafeAt, +\* DidNotVoteIn, VotedFor + PROOF OMITTED + <4>. SUFFICES ASSUME P + PROVE \E votesp, maxBalp: + /\ b > maxBal[self] + /\ maxBalp = [maxBal EXCEPT ![self] = b] + /\ votesp = votes + /\ <> # <> + BY <4>1 + <4> WITNESS votes, [maxBal EXCEPT ![self] = b] + <4>. QED BY QA DEF VInv, TypeOK, Ballot + <3>. QED BY <3>1, <3>2, <3>3, PTL + <2>3. QQ /\ [][LNext]_vars => []QQ + <3>1. QQ /\ [LNext]_vars => QQ' + BY <1>3, <1>4 + <3>. QED BY <3>1, PTL + <2>4. []QQ => [](maxBal[self] = b) + BY PTL + <2>5. LiveAssumption!(Q,b) => WF_vars(A) + BY Isa + <2>6. Spec => LInv1 + <3>1. Init => VInv + BY InitImpliesInv + <3>2. Init => LNInv1 + BY QA DEF Init, Ballot + <3>. QED BY <3>1, <3>2 DEF Spec + <2>. QED + BY <2>2, <2>3, <2>4, <2>5, <2>6, <1>2, PTL + +<1> DEFINE LNInv2 == \A a \in Q : maxBal[a] = b + LInv2 == VInv /\ LNInv2 + +<1>6. LInv2 /\ [LNext]_vars => LInv2' + BY <1>4, QuorumNonEmpty + +<1>7. Spec /\ LiveAssumption!(Q, b) => <>[](chosen # {}) + <2> DEFINE Voted(a) == \E v \in Value : VotedFor(a, b, v) + <2>1. Spec /\ LiveAssumption!(Q, b) => <>[]LInv2 + <3>1. Spec /\ LiveAssumption!(Q,b) => <>[]LNInv2 +\* BY <1>5 \* doesn't check + PROOF OMITTED + <3>. QED BY <3>1, VT2, PTL + <2>2. LInv2 /\ (\A a \in Q : Voted(a)) => (chosen # {}) + <3>1. SUFFICES ASSUME LInv2, + \A a \in Q : Voted(a) + PROVE chosen # {} + OBVIOUS + <3>2. \E v \in Value : \A a \in Q : VotedFor(a, b, v) + <4>2. PICK a0 \in Q, v \in Value : VotedFor(a0, b, v) + BY <3>1, QuorumNonEmpty + <4>3. ASSUME NEW a \in Q + PROVE VotedFor(a, b, v) + BY <3>1, <4>2, QA DEF VInv, VInv3 + <4>4. QED + BY <4>3 + <3>3. QED + BY <3>2 DEF chosen, ChosenIn + <2>3. Spec /\ LiveAssumption!(Q, b) => (\A a \in Q : <>[]Voted(a)) + <3>1. SUFFICES ASSUME NEW self \in Q + PROVE Spec /\ LiveAssumption!(Q, b) => <>[]Voted(self) +\* OBVIOUS \* doesn't check?! + PROOF OMITTED + <3>2. Spec /\ LiveAssumption!(Q, b) => <>Voted(self) + <4>2. [][LNext]_vars /\ WF_vars(BallotAction(self, b)) + => ((LInv2 /\ ~Voted(self)) ~> LInv2 /\ Voted(self)) + <5> DEFINE P == LInv2 /\ ~Voted(self) + QQ == LInv2 /\ Voted(self) + A == BallotAction(self, b) + <5>1. P /\ [LNext]_vars => (P' \/ QQ') + BY <1>6 + <5>2. P /\ <>_vars => QQ' + <6>1. SUFFICES ASSUME P, + LNext, + A + PROVE QQ' + OBVIOUS + <6>2. CASE \E v \in Value : VoteFor(self, b, v) + BY <6>1, <6>2, <5>1, QA, Zenon DEF VoteFor, Voted, VotedFor, LInv2, VInv, TypeOK + <6>3. CASE IncreaseMaxBal(self, b) + BY <6>1, <6>3 DEF IncreaseMaxBal, Ballot + <6>4. QED + BY <6>1, <6>2, <6>3 DEF BallotAction + <5>3. P => ENABLED <>_vars + <6>1. SUFFICES ASSUME P + PROVE ENABLED <>_vars + OBVIOUS + <6>2. (ENABLED <>_vars) <=> + \E votesp, maxBalp : + /\ \/ /\ b > maxBal[self] + /\ maxBalp = [maxBal EXCEPT ![self] = b] + /\ votesp = votes + \/ \E v \in Value : + /\ maxBal[self] \leq b + /\ DidNotVoteIn(self, b) + /\ \A p \in Acceptor \ {self} : + \A w \in Value : VotedFor(p, b, w) => (w = v) + /\ SafeAt(b, v) + /\ votesp = [votes EXCEPT ![self] = votes[self] + \cup {<>}] + /\ maxBalp = [maxBal EXCEPT ![self] = b] + /\ <> # <> +\* BY DEF BallotAction, IncreaseMaxBal, VoteFor, vars, SafeAt, +\* DidNotVoteIn, VotedFor + PROOF OMITTED + <6> SUFFICES + \E votesp, maxBalp: + /\ \E v \in Value : + /\ maxBal[self] \leq b + /\ DidNotVoteIn(self, b) + /\ \A p \in Acceptor \ {self} : + \A w \in Value : VotedFor(p, b, w) => (w = v) + /\ SafeAt(b, v) + /\ votesp = [votes EXCEPT ![self] = votes[self] + \cup {<>}] + /\ maxBalp = [maxBal EXCEPT ![self] = b] + /\ <> # <> + BY <6>2 + <6> DEFINE someVoted == \E p \in Acceptor \ {self} : + \E w \in Value : VotedFor(p, b, w) + vp == CHOOSE p \in Acceptor \ {self} : + \E w \in Value : VotedFor(p, b, w) + vpval == CHOOSE w \in Value : VotedFor(vp, b, w) + <6>3. someVoted => /\ vp \in Acceptor + /\ vpval \in Value + /\ VotedFor(vp, b, vpval) + BY Zenon + <6> DEFINE v == IF someVoted THEN vpval + ELSE CHOOSE v \in Value : SafeAt(b, v) + <6>4. (v \in Value) /\ SafeAt(b, v) + BY <6>1, <6>3, VT4 DEF VInv, VInv2, Ballot + <6> DEFINE votesp == [votes EXCEPT ![self] = votes[self] \cup {<>}] + maxBalp == [maxBal EXCEPT ![self] = b] + <6> WITNESS votesp, maxBalp + <6> SUFFICES /\ maxBal[self] \leq b + /\ DidNotVoteIn(self, b) + /\ \A p \in Acceptor \ {self} : + \A w \in Value : VotedFor(p, b, w) => (w = v) + /\ votesp # votes + BY <6>4, Zenon + <6>5. maxBal[self] \leq b + BY <6>1 DEF Ballot + <6>6. DidNotVoteIn(self, b) + BY <6>1 DEF Voted, DidNotVoteIn + <6>7. ASSUME NEW p \in Acceptor \ {self}, + NEW w \in Value, + VotedFor(p, b, w) + PROVE w = v + BY <6>7, <6>3, <6>1 DEF VInv, VInv3 + <6>8. votesp # votes + <7>1. votesp[self] = votes[self] \cup {<>} + BY <6>1, QA DEF LInv2, VInv, TypeOK + <7>2. \A w \in Value : <> \notin votes[self] + BY <6>6 DEF DidNotVoteIn, VotedFor + <7>3. QED + BY <7>1, <7>2, <6>4, Zenon + <6>9. QED + BY <6>5, <6>6, <6>7, <6>8, Zenon + <5>4. QED + BY <5>1, <5>2, <5>3, PTL + <4>3. []LInv2 /\ ((LInv2 /\ ~Voted(self)) ~> LInv2 /\ Voted(self)) + => <>Voted(self) + BY PTL + <4>4. LiveAssumption!(Q, b) => WF_vars(BallotAction(self,b)) + BY Isa + <4>. QED + BY <1>2, <2>1, <4>2, <4>3, <4>4, PTL + <3>3. Spec => [](Voted(self) => []Voted(self)) + <4>1. (VInv /\ Voted(self)) /\ [Next]_vars => (VInv /\ Voted(self))' + <5> SUFFICES ASSUME VInv, Voted(self), [Next]_vars + PROVE VInv' /\ Voted(self)' + OBVIOUS + <5>1. VInv' + BY InductiveInvariance + <5>2. Voted(self)' + <6> CASE vars' = vars + BY DEF vars, Voted, VotedFor + <6> CASE Next + <7>2. PICK a \in Acceptor, c \in Ballot : BallotAction(a, c) + BY NextDef DEF VInv + <7>3. CASE IncreaseMaxBal(a, c) + BY <7>3 DEF IncreaseMaxBal, Voted, VotedFor + <7>4. CASE \E v \in Value : VoteFor(a, c, v) + BY <7>4, QA DEF VInv, TypeOK, VoteFor, Voted, VotedFor + <7>5. QED + BY <7>2, <7>3, <7>4 DEF BallotAction + <6> QED + OBVIOUS + <5>3. QED + BY <5>1, <5>2 + <4>3. QED + BY <4>1, VT2, PTL DEF Spec + <3>4. QED + BY <3>2, <3>3, PTL + <2>4. (\A a \in Q : <>[]Voted(a)) => <>[](\A a \in Q : Voted(a)) +\* BY <1>a, EventuallyAlwaysForall \* doesn't check + PROOF OMITTED + <2>. QED + BY <2>1, VT2, <2>2, <2>3, <2>4, PTL + +<1>. QED + <2>1. Spec /\ LiveAssumption!(Q, b) => C!Spec /\ <>[](chosen # {}) + BY VT3, <1>7, Isa + <2>2. Spec /\ LiveAssumption!(Q, b) => C!Spec /\ []<>(chosen # {}) + BY <2>1, PTL + <2>. QED + BY <2>2, <1>1, Isa + +=============================================================================== +\* Modification History +\* Last modified Fri Jul 24 18:20:31 CEST 2020 by merz +\* Last modified Wed Apr 29 12:24:23 CEST 2020 by merz +\* Last modified Mon May 28 08:53:38 PDT 2012 by lamport + + diff --git a/specifications/transaction_commit/2PCwithBTM.cfg b/specifications/transaction_commit/2PCwithBTM.cfg new file mode 100644 index 00000000..abb23179 --- /dev/null +++ b/specifications/transaction_commit/2PCwithBTM.cfg @@ -0,0 +1,7 @@ +CONSTANTS + RM = {rm1, rm2, rm3} + RMMAYFAIL = TRUE + TMMAYFAIL = TRUE +INVARIANTS TypeOK Consistency +SPECIFICATION Spec + diff --git a/specifications/transaction_commit/2PCwithBTM.tla b/specifications/transaction_commit/2PCwithBTM.tla new file mode 100644 index 00000000..80d8432e --- /dev/null +++ b/specifications/transaction_commit/2PCwithBTM.tla @@ -0,0 +1,196 @@ +----------------------------- MODULE 2PCwithBTM ------------------------------ +EXTENDS Integers, Sequences, FiniteSets, TLC +CONSTANT RM, \* The set of participating resource managers RM=1..3 + RMMAYFAIL, + TMMAYFAIL \* Whether TM may fail MAYFAIL=TRUE or FALSE +(*************************************************************************** +A modified version of P2TCommit at http://lamport.azurewebsites.net/tla/two-phase.html +Transaction manager (TM) is added. + + `. +--algorithm TransactionCommit { + variable rmState = [rm \in RM |-> "working"], + tmState = "init"; + define { + canCommit == \A rmc \in RM: rmState[rmc] \in {"prepared"} + \/ \E rm \in RM : rmState[rm] \in {"committed"} \* for when BTM takes over + canAbort == \E rm \in RM : rmState[rm] \in {"aborted","failed"} + /\ ~\E rmc \in RM : rmState[rmc]= "committed" \* inconsistent if commented + } + macro Prepare(p) { + await rmState[p] = "working"; + rmState[p] := "prepared" ; } + + macro Decide(p) { + either { await tmState="commit"; + rmState[p] := "committed";} + + or { await rmState[p]="working" \/ tmState="abort"; + rmState[p] := "aborted";} + } + + macro Fail(p) { + if (RMMAYFAIL) rmState[p] := "failed"; + } + + fair process (RManager \in RM) { + RS: while (rmState[self] \in {"working", "prepared"}) { + either Prepare(self) or Decide(self) or Fail(self)} + } + + fair process (TManager=0) { + TS:either{ await canCommit; + TC: tmState := "commit"; + F1: if (TMMAYFAIL) tmState := "hidden";} + + or { await canAbort; + TA: tmState := "abort"; + F2: if (TMMAYFAIL) tmState := "hidden";} + } + + fair process (BTManager=10) { +BTS:either{await canCommit /\ tmState="hidden"; + BTC: tmState := "commit";} + + or { await canAbort /\ tmState="hidden"; + BTA: tmState := "abort";} + } +} + .' + + ***************************************************************************) +\* BEGIN TRANSLATION +VARIABLES rmState, tmState, pc + +(* define statement *) +canCommit == \A rmc \in RM: rmState[rmc] \in {"prepared"} + \/ \E rm \in RM : rmState[rm] \in {"committed"} +canAbort == \E rm \in RM : rmState[rm] \in {"aborted","failed"} + /\ ~\E rmc \in RM : rmState[rmc]= "committed" + + +vars == << rmState, tmState, pc >> + +ProcSet == (RM) \cup {0} \cup {10} + +Init == (* Global variables *) + /\ rmState = [rm \in RM |-> "working"] + /\ tmState = "init" + /\ pc = [self \in ProcSet |-> CASE self \in RM -> "RS" + [] self = 0 -> "TS" + [] self = 10 -> "BTS"] + +RS(self) == /\ pc[self] = "RS" + /\ IF rmState[self] \in {"working", "prepared"} + THEN /\ \/ /\ rmState[self] = "working" + /\ rmState' = [rmState EXCEPT ![self] = "prepared"] + \/ /\ \/ /\ tmState="commit" + /\ rmState' = [rmState EXCEPT ![self] = "committed"] + \/ /\ rmState[self]="working" \/ tmState="abort" + /\ rmState' = [rmState EXCEPT ![self] = "aborted"] + \/ /\ IF RMMAYFAIL /\ ~\E rm \in RM:rmState[rm]="failed" + THEN /\ rmState' = [rmState EXCEPT ![self] = "failed"] + ELSE /\ TRUE + /\ UNCHANGED rmState + /\ pc' = [pc EXCEPT ![self] = "RS"] + ELSE /\ pc' = [pc EXCEPT ![self] = "Done"] + /\ UNCHANGED rmState + /\ UNCHANGED tmState + +RManager(self) == RS(self) + +TS == /\ pc[0] = "TS" + /\ \/ /\ canCommit + /\ pc' = [pc EXCEPT ![0] = "TC"] + \/ /\ canAbort + /\ pc' = [pc EXCEPT ![0] = "TA"] + /\ UNCHANGED << rmState, tmState >> + +TC == /\ pc[0] = "TC" + /\ tmState' = "commit" + /\ pc' = [pc EXCEPT ![0] = "F1"] + /\ UNCHANGED rmState + +F1 == /\ pc[0] = "F1" + /\ IF TMMAYFAIL + THEN /\ tmState' = "hidden" + ELSE /\ TRUE + /\ UNCHANGED tmState + /\ pc' = [pc EXCEPT ![0] = "Done"] + /\ UNCHANGED rmState + +TA == /\ pc[0] = "TA" + /\ tmState' = "abort" + /\ pc' = [pc EXCEPT ![0] = "F2"] + /\ UNCHANGED rmState + +F2 == /\ pc[0] = "F2" + /\ IF TMMAYFAIL + THEN /\ tmState' = "hidden" + ELSE /\ TRUE + /\ UNCHANGED tmState + /\ pc' = [pc EXCEPT ![0] = "Done"] + /\ UNCHANGED rmState + +TManager == TS \/ TC \/ F1 \/ TA \/ F2 + +BTS == /\ pc[10] = "BTS" + /\ \/ /\ canCommit /\ tmState="hidden" + /\ pc' = [pc EXCEPT ![10] = "BTC"] + \/ /\ canAbort /\ tmState="hidden" + /\ pc' = [pc EXCEPT ![10] = "BTA"] + /\ UNCHANGED << rmState, tmState >> + +BTC == /\ pc[10] = "BTC" + /\ tmState' = "commit" + /\ pc' = [pc EXCEPT ![10] = "Done"] + /\ UNCHANGED rmState + +BTA == /\ pc[10] = "BTA" + /\ tmState' = "abort" + /\ pc' = [pc EXCEPT ![10] = "Done"] + /\ UNCHANGED rmState + +BTManager == BTS \/ BTC \/ BTA + +Next == TManager \/ BTManager + \/ (\E self \in RM: RManager(self)) + \/ (* Disjunct to prevent deadlock on termination *) + ((\A self \in ProcSet: pc[self] = "Done") /\ UNCHANGED vars) + +Spec == /\ Init /\ [][Next]_vars + /\ \A self \in RM : WF_vars(RManager(self)) + /\ WF_vars(TManager) + /\ WF_vars(BTManager) + +Termination == <>(\A self \in ProcSet: pc[self] = "Done") + +\* END TRANSLATION + +(***************************************************************************) +(* The invariants: *) +(***************************************************************************) +TypeOK == + (*************************************************************************) + (* The type-correctness invariant *) + (*************************************************************************) + /\ rmState \in [RM -> {"working", "prepared", "committed", "aborted", "failed"}] + /\ tmState \in {"init", "commit", "abort", "hidden"} + +Consistency == + (*************************************************************************) + (* A state predicate asserting that two RMs have not arrived at *) + (* conflicting decisions. *) + (*************************************************************************) + \A rm1, rm2 \in RM : ~ /\ rmState[rm1] = "aborted" + /\ rmState[rm2] = "committed" + + +NotCommitted == \A rm \in RM : rmState[rm] # "committed" + +============================================================================= +\* Modification History +\* Last modified Wed Dec 13 14:34:34 EST 2017 by mad +\* Last modified Fri Nov 17 12:18:24 EST 2017 by murat +\* Last modified Tue Oct 11 08:14:15 PDT 2011 by lamport +\* Created Mon Oct 10 05:31:02 PDT 2011 by lamport