Skip to content
93 changes: 93 additions & 0 deletions gherkin/scp/sequencer.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
Feature: Sequencer
Once a StartInstance message is received, the sequencer filter its own transactions, sets a timer,
and begins simulating with a mailbox tracer.
It exchange messages with other sequencers and,
once the simulation produces a result, it votes and waits for a decision.

Background:
Given there is a chain "1" with sequencer "A"
Given there is a chain "2" with sequencer "B"

Scenario: Transaction filtering and timer
When sequencer "A" receives StartInstance with instance ID "0x1", period ID "1", sequence number "1", and xtrequest "1: [tx1_1,tx1_2], 2: [tx2]"
Then sequencer "A" should filter the transactions "[tx1_1,tx1_2]"
And sequencer "A" should start a timer for the instance "0x1"

Scenario: No transaction raises error
When sequencer "A" receives StartInstance with instance ID "0x1", period ID "1", sequence number "1", and xtrequest "2: [tx2], 3: [tx3]"
Then error "No transactions for this chain" should be returned

Scenario: Simulation raises error different from read miss
Given sequencer "A" receives StartInstance with instance ID "0x1", period ID "2", sequence number "2", and xtrequest "1: [tx1], 2: [tx2]"
When the execution engine simulates "tx1" and returns an error different than "Read miss"
Then sequencer "A" should send Vote to the shared publisher with instance ID "0x1", chain ID "1", and vote "false"

Scenario: Simulation successful
Given sequencer "A" receives StartInstance with instance ID "0x1", period ID "2", sequence number "2", and xtrequest "1: [tx1], 2: [tx2]"
When the execution engine simulates "tx1" and returns success
Then sequencer "A" should send Vote to the shared publisher with instance ID "0x1", chain ID "1", and vote "true"

Scenario: Read miss handled by storing expected message
Given sequencer "A" receives StartInstance with instance ID "0x1", period ID "2", sequence number "2", and xtrequest "1: [tx1], 2: [tx2]"
When the execution engine simulates "tx1" and returns a read miss for message with source chain ID "2", destination chain ID "1", source "0xabc", receiver "0xdef", session ID "0x123", label "MSG", and data "data"
Then sequencer "A" should store mailbox message with source chain ID "2", destination chain ID "1", source "0xabc", receiver "0xdef", session ID "0x123", label "MSG", and data "data" as expected message for instance ID "0x1"

Scenario: Written message is sent to the destination sequencer
Given sequencer "A" receives StartInstance with instance ID "0x1", period ID "3", sequence number "9", and xtrequest "1: [tx1], 2: [tx2]"
When the execution engine simulates "tx1" and emits a MailboxMessage with source chain "1", destination chain "2", source "0xaaa", receiver "0xbbb", session ID "0x777", label "TRANSFER", and data "[0x01,0x02]"
Then sequencer "A" should send MailboxMessage to sequencer "B" with instance ID "0x1", source chain "1", destination chain "2", source "0xaaa", receiver "0xbbb", session ID "0x777", label "TRANSFER", and data "[0x01,0x02]"

Scenario: A received mailbox message is stored as pending
Given sequencer "A" receives StartInstance with instance ID "0x1", period ID "3", sequence number "9", and xtrequest "1: [tx1], 2: [tx2]"
When sequencer "A" receives MailboxMessage with instance ID "0x1", source chain "2", destination chain "1", source "0x222", receiver "0x111", session ID "0x999", label "NOTE", and data "[0xab]"
Then sequencer "A" should append to the pending mailbox queue for instance ID "0x1" the message with source chain "2", destination chain "1", source "0x222", receiver "0x111", session ID "0x999", label "NOTE", and data "[0xab]"
And sequencer "A" should not trigger a new simulation

Scenario: A received mailbox message that is expected is added to the inbox and triggers a new simulation
Given sequencer "A" receives StartInstance with instance ID "0x1", period ID "2", sequence number "2", and xtrequest "1: [tx1], 2: [tx2]"
And the execution engine simulates "tx1" and returns a read miss for message with source chain ID "2", destination chain ID "1", source "0xabc", receiver "0xdef", session ID "0x123", label "MSG", and data "data"
And sequencer "A" should store mailbox message with source chain ID "2", destination chain ID "1", source "0xabc", receiver "0xdef", session ID "0x123", label "MSG", and data "data" as expected message for instance ID "0x1"
When sequencer "A" receives MailboxMessage with instance ID "0x1", source chain "2", destination chain "1", source "0xabc", receiver "0xdef", session ID "0x123", label "MSG", and data "data"
Then the sequencer should insert a mailbox.putInbox transaction above the transaction list for the message with source chain "2", destination chain "1", source "0xabc", receiver "0xdef", session ID "0x123", label "MSG", and data "data"
And sequencer "A" should start a new simulation

Scenario: Timeout before voting causes a rejection
Given sequencer "A" receives StartInstance with instance ID "0x1", period ID "2", sequence number "2", and xtrequest "1: [tx1], 2: [tx2]"
When the timer for instance ID "0x1" expires
Then sequencer "A" should send Vote to the shared publisher with instance ID "0x1", chain ID "1", and vote "false"
And sequencer "A" should mark the instance as rejected

Scenario Outline: Timeout after voting causes no action
Given sequencer "A" receives StartInstance with instance ID "0x1", period ID "2", sequence number "2", and xtrequest "1: [tx1], 2: [tx2]"
And sequencer "A" sent Vote to the shared publisher with instance ID "0x1", chain ID "1", and vote "<vote>"
When the timer for instance ID "0x1" expires
Then no additional Vote message should be sent

Examples:
| vote |
| true |
| false |

Scenario: Decision false causes rejection
Given sequencer "A" receives StartInstance with instance ID "0x1", period ID "2", sequence number "2", and xtrequest "1: [tx1], 2: [tx2]"
When sequencer "A" receives Decided from the shared publisher with instance ID "0x1" and decision "false"
Then sequencer "A" should mark the instance as rejected

Scenario: If decision true is received but no vote was sent, an error is raised
Given sequencer "A" receives StartInstance with instance ID "0x1", period ID "2", sequence number "2", and xtrequest "1: [tx1], 2: [tx2]"
And sequencer "A" has not sent Vote to the shared publisher with instance ID "0x1"
When sequencer "A" receives Decided from the shared publisher with instance ID "0x1" and decision "true"
Then error "decision true but no vote sent is an impossible state" should be returned

Scenario: Decision true causes finalization
Given sequencer "A" receives StartInstance with instance ID "0x1", period ID "2", sequence number "2", and xtrequest "1: [tx1], 2: [tx2]"
And the execution engine simulates "tx1" and returns success
And sequencer "A" sent Vote to the shared publisher with instance ID "0x1", chain ID "1", and vote "true"
When sequencer "A" receives Decided from the shared publisher with instance ID "0x1" and decision "true"
Then sequencer "A" should accept the instance

Scenario: Double decided is ignored and error is raised
Given sequencer "A" receives StartInstance with instance ID "0x1", period ID "2", sequence number "2", and xtrequest "1: [tx1], 2: [tx2]"
And sequencer "A" receives Decided from the shared publisher with instance ID "0x1" and decision "false"
When sequencer "A" receives Decided from the shared publisher with instance ID "0x1" and decision "true"
Then error "instance already decided" should be returned
247 changes: 247 additions & 0 deletions gherkin/scp/sequencer_2.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,247 @@
Feature: Sequencer
Once the sequencer receives a StartInstance message it filters its own transactions, sets a timer,
and begins simulating with a mailbox tracer. It exchanges messages with peer sequencers and,
after the simulation completes, it votes and waits for a decision.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note it is also possible to put tags here


Background:
Given there is a chain "1" with sequencer "A"
And there is a chain "2" with sequencer "B"

@start-instance
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My thoughts were actually to divide the sequencer into files: start_instance.feature, timeout.feature, and etc.

Then we in the Feature section we have a place to document each feature.
So the tests become easy way to learn how each feature of the spec works.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Scenario: Filters local transactions and starts a timer
When sequencer "A" receives StartInstance:
"""
instance_id: 0x1
period_id: 1
sequence_number: 1
xtrequest:
1: [tx1_1, tx1_2]
2: [tx2]
"""
Then sequencer "A" should only consider transactions "[tx1_1,tx1_2]" for simulation
And a timer for instance "0x1" should start

@start-instance
Scenario: Rejects StartInstance with no local transactions
When sequencer "A" receives StartInstance:
"""
instance_id: 0x1
period_id: 1
sequence_number: 1
xtrequest:
2: [tx2]
3: [tx3]
"""
Then an error occurs:
"""
No transactions for this chain
"""

@simulation
Scenario Outline: Votes according to simulation outcome
Given sequencer "A" receives StartInstance:
"""
instance_id: 0x1
period_id: 2
sequence_number: 2
xtrequest:
1: [tx1]
2: [tx2]
"""
When the execution engine simulates "tx1" and <result>
Then sequencer "A" should publish Vote with:
| field | value |
| instance_id | 0x1 |
| chain_id | 1 |
| vote | <vote> |

Examples:
| result | vote |
| returns success | true |
| returns an error other than "Read miss" | false |
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: maybe the word "returns" is redundant

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed "returns"


@mailbox @simulation
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you are mocking the simulation here, right?
So I disagree with this tag

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no, just invoking the simulation

Scenario: Records expected mailbox message after read miss
Given sequencer "A" receives StartInstance:
"""
instance_id: 0x1
period_id: 2
sequence_number: 2
xtrequest:
1: [tx1]
2: [tx2]
"""
When the execution engine simulates "tx1" and returns a read miss for mailbox message:
| field | value |
| source_chain_id | 2 |
| destination_chain_id | 1 |
| source | 0xabc |
| receiver | 0xdef |
| session_id | 0x123 |
| label | MSG |
| data | data |
Then sequencer "A" should record that mailbox message as expected for instance "0x1"

@mailbox
Scenario: Sends written mailbox message to the destination sequencer
Given sequencer "A" receives StartInstance:
"""
instance_id: 0x1
period_id: 3
sequence_number: 9
xtrequest:
1: [tx1]
2: [tx2]
"""
When the execution engine simulates "tx1" and emits MailboxMessage:
| field | value |
| source_chain | 1 |
| destination_chain | 2 |
| source | 0xaaa |
| receiver | 0xbbb |
| session_id | 0x777 |
| label | TRANSFER |
| data | [0x01,0x02] |
Then sequencer "A" should forward that MailboxMessage to sequencer "B" with instance ID "0x1"

@mailbox
Scenario Outline: Handles inbound mailbox messages based on expectation
Given sequencer "A" receives StartInstance:
"""
instance_id: 0x1
period_id: 2
sequence_number: 2
xtrequest:
1: [tx1]
2: [tx2]
"""
And sequencer "A" <expected_state> an expected mailbox message with:
| field | value |
| source_chain | 2 |
| destination_chain | 1 |
| source | 0xabc |
| receiver | 0xdef |
| session_id | 0x123 |
| label | MSG |
| data | data |
When sequencer "A" receives MailboxMessage with the same payload and instance ID "0x1"
Then <storage_result>
And <simulation_effect>

Examples:
| expected_state | storage_result | simulation_effect |
| has not stored | the message is appended to the pending mailbox queue for instance "0x1" | sequencer "A" should not start a new simulation |
| has already stored | the message is moved to the inbox and a mailbox.putInbox transaction is inserted before others | sequencer "A" should start a new simulation |

@timeout
Scenario: Rejects instance when timer expires before voting
Given sequencer "A" receives StartInstance:
"""
instance_id: 0x1
period_id: 2
sequence_number: 2
xtrequest:
1: [tx1]
2: [tx2]
"""
When the timer for instance ID "0x1" expires
Then sequencer "A" should publish Vote with:
| field | value |
| instance_id | 0x1 |
| chain_id | 1 |
| vote | false |
And sequencer "A" should mark the instance "0x1" as rejected

@timeout
Scenario Outline: Ignores timer expiry after voting
Given sequencer "A" receives StartInstance:
"""
instance_id: 0x1
period_id: 2
sequence_number: 2
xtrequest:
1: [tx1]
2: [tx2]
"""
And sequencer "A" previously published Vote with:
| field | value |
| instance_id | 0x1 |
| chain_id | 1 |
| vote | <vote> |
When the timer for instance ID "0x1" expires
Then no additional Vote message should be sent

Examples:
| vote |
| true |
| false |

@decision
Scenario: Rejects instance when decision is false
Given sequencer "A" receives StartInstance:
"""
instance_id: 0x1
period_id: 2
sequence_number: 2
xtrequest:
1: [tx1]
2: [tx2]
"""
When sequencer "A" receives Decided for instance "0x1" with decision "false"
Then sequencer "A" should mark the instance "0x1" as rejected

@decision
Scenario: Errors when decision true arrives without a prior vote
Given sequencer "A" receives StartInstance:
"""
instance_id: 0x1
period_id: 2
sequence_number: 2
xtrequest:
1: [tx1]
2: [tx2]
"""
And sequencer "A" has not published a Vote for instance "0x1"
When sequencer "A" receives Decided for instance "0x1" with decision "true"
Then an error occurs:
"""
decision true but no vote sent is an impossible state
"""

@decision
Scenario: Finalizes instance when decision true matches prior vote
Given sequencer "A" receives StartInstance:
"""
instance_id: 0x1
period_id: 2
sequence_number: 2
xtrequest:
1: [tx1]
2: [tx2]
"""
And the execution engine simulates "tx1" and returns success
And sequencer "A" previously published Vote with:
| field | value |
| instance_id | 0x1 |
| chain_id | 1 |
| vote | true |
When sequencer "A" receives Decided for instance "0x1" with decision "true"
Then sequencer "A" should accept the instance "0x1"

@decision
Scenario: Raises error when a decided instance receives a second decision
Given sequencer "A" receives StartInstance:
"""
instance_id: 0x1
period_id: 2
sequence_number: 2
xtrequest:
1: [tx1]
2: [tx2]
"""
And sequencer "A" receives Decided for instance "0x1" with decision "false"
When sequencer "A" later receives Decided for instance "0x1" with decision "true"
Then an error occurs:
"""
instance already decided
"""
Loading