proposed core.account-verification

Verify an account's address after its correct password

A correct password proved an account whose address is not proved yet. The engine mails a 6-digit code to the transaction's identifier through the capped sender of PRO-14 and asks for it. A right code, answered outside the email-code ladder's wait and within 10 minutes, marks the address verified, and the sign-in goes on with no amr added. A wrong code is refused and counted, in the transaction and on the ladder; the 5th wrong answer of a transaction burns the code and ends the transaction. An answer without a code asks for a new one, once a minute.

What this view shows

the email_code step with the purpose verify_account, from the first code after a correct password to the address marked verified, round by round.

Open world: what is not drawn here is not ruled out. Only the guarantees below are binding.

Left out of this view:

The main story

When: The send caps let the code through, or drop its mail quietly at the address-and-IP tier, the code was issued less than 10 minutes ago, the last code was issued less than a minute ago, the identifier is outside a wait of the email-code ladder, the account is the subject's, enabled, with its password credential.

  1. Issue a code and ask the capped sender to mail it: the code is accepted: its HMAC is kept, its mail queued, and the floor is waited out
  2. Ask for the code: the person types the code from the mail
  3. Take the round's answer to the email_code step: the code is live
  4. Reserve the answer on the email-code ladder: the attempt is reserved
  5. Compare the answer with the held code, in constant time: the code is right: the reservation settles as a success
  6. Mark the address verified: the address is verified: the code is consumed, email_code joins the transaction's verified steps with no amr, and the step is audited as passed under the subject

Result: Go on with the sign-in, the address verified.

The flow

a tier that answers 429 is full: nothing is mailed, a code the transaction held stays, send_capped is audited, and the floor is waited out the code is accepted: its HMAC is kept, its mail queued, and the floor is waited out the code has passed its 10 minutes: refused with code_expired, audited, neither compared nor counted the code is live less than a minute has passed: the same step, nothing mailed, and the floor is waited out a new code is due; the count of wrong answers stays the identifier is inside a wait: refused, neither compared nor counted, audited as locked_out the attempt is reserved the code is right: the reservation settles as a success the 5th wrong answer of the transaction: the code is burned, the transaction ends, and code_burned is audited under the subject the first wrong answer: refused with invalid_credentials, audited as invalid_code the second wrong answer: refused alike the third wrong answer: refused alike the fourth wrong answer: refused alike; the next wrong one burns the code the account was disabled or changed since the read: nothing is written, and the transaction ends the address is verified: the code is consumed, email_code joins the transaction's verified steps with no amr, and the step is audited as passed under the subject the next round carries no answer: the same step, nothing mailed the person types the code from the mail the person types another code the person asks for a new code: an answer with no otp the otp is not of the offered length: refused as invalid_answer, nothing compared or counted the next round carries no answer the person types the right code the person types another wrong code the person asks for a new code the otp is not of the offered length the next round carries no answer: the same step and its error, nothing audited the person asks for a new code the person types the expired code again the person types another code the otp is not of the offered length the code has passed its 10 minutes: refused with code_expired, neither compared nor counted the code is live the identifier is inside a wait: refused, neither compared nor counted the attempt is reserved start Six digits from a CSPRNG, and a salt of its own. The send caps of PRO-14 count the request before anything is kept, with the code after a correct first factor, so it skips the per-address ceiling and counts toward its own cap. An accepted code replaces the transaction's code, if any, by its salt and HMAC, and keeps the count of wrong answers; its mail is dispatched after the answer is built. A capped one keeps nothing new. Issue a code and ask the capped sender to mail it issuingCode · server step The step grammar has read it against the offered params: an otp of exactly the offered length, or none, which asks for a new code. An answer to another step is ignored, as every eager answer is. Take the round's answer to the email_code step takingAnswer · server step A new code may be asked for one minute after the last one, and the send caps still count it. Ask whether a new code is due checkingResend · server step The ladder hook of PRO-12 with the email-code counter, keyed on the identifier HMAC: one atomic operation reads the wait, refuses an attempt inside it, and otherwise counts the attempt as a failure until it settles. A store error refuses the round before the code is compared. Reserve the answer on the email-code ladder reservingCodeAttempt · server step The answer's HMAC under the code's salt against the held one, in constant time. The right code settles the reservation as a success, which clears the email-code counter and no other: never the password's. Compare the answer with the held code, in constant time comparingCode · server step The answer's HMAC differs from the held one: the reservation settles as a failure, and the transaction counts one wrong answer more. The 5th burns the code. Compare a wrong answer with the held code, in constant time comparingWrongCode · server step In one database transaction, the password credential's email_verified and the email attribute's verified flag, for the subject's enabled account only. The same person proved the password and the mailbox, so every credential stays. A store error refuses the round: the code stays live, and the same code may be answered again. Mark the address verified markingVerified · server step The params say why the code was sent, how many digits it has, how long it stays valid and when another may be asked for; the personal data is the address it went to, the transaction's identifier. Ask for the code offeringCode · server step The same step with invalid_credentials; the code stays, and so does the count of wrong answers. Ask for the code again codeRefused · server step The same step with code_expired: nothing was compared or counted. A new code is one resend away, once its minute has passed, which it has. Say the code has expired codeExpired · server step As takingAnswer, for an answer that is not the held code. Take a wrong answer to the email_code step takingWrongAnswer · server step As reservingCodeAttempt, for an answer that is not the held code. Reserve a wrong answer on the email-code ladder reservingWrongAttempt · server step The engine goes on with rule 8: a second factor the MFA policy requires and the account has (#653), else the sign-in completes. The completed sign-in's amr is the password's alone and its auth_time the password's. Go on with the sign-in, the address verified ends here The same 429 for every address. The transaction stays as far as the round took it: a code it held stays live. Refuse the round for the send caps ends here Neither compared nor counted; the transaction and its code stay. Refuse the answer inside the email-code ladder's wait ends here Five wrong answers in one transaction are its budget for codes. The transport discards the transaction; the Interaction API starts the sign-in again, and the email-code ladder counts on across transactions. Burn the code and end the transaction ends here The account was disabled or changed between the round's read and the write; nothing was written. End the transaction for an account that changed ends here A fault of the request, audited as invalid_answer; the transaction and its code stay. Refuse a malformed answer ends here
a tier that answers 429 is full: nothing is mailed, a code the transaction held stays, send_capped is audited, and the floor is waited out the code is accepted: its HMAC is kept, its mail queued, and the floor is waited out fails: the send-cap store fails: nothing is mailed or kept; the floor is waited out the code has passed its 10 minutes: refused with code_expired, audited, neither compared nor counted the code is live less than a minute has passed: the same step, nothing mailed, and the floor is waited out a new code is due; the count of wrong answers stays the identifier is inside a wait: refused, neither compared nor counted, audited as locked_out the attempt is reserved fails: the ladder store fails; nothing is compared the code is right: the reservation settles as a success the 5th wrong answer of the transaction: the code is burned, the transaction ends, and code_burned is audited under the subject the first wrong answer: refused with invalid_credentials, audited as invalid_code the second wrong answer: refused alike the third wrong answer: refused alike the fourth wrong answer: refused alike; the next wrong one burns the code the account was disabled or changed since the read: nothing is written, and the transaction ends the address is verified: the code is consumed, email_code joins the transaction's verified steps with no amr, and the step is audited as passed under the subject fails: the database fails: nothing is written, and the code stays live the next round carries no answer: the same step, nothing mailed the person types the code from the mail the person types another code the person asks for a new code: an answer with no otp the otp is not of the offered length: refused as invalid_answer, nothing compared or counted the next round carries no answer the person types the right code the person types another wrong code the person asks for a new code the otp is not of the offered length the next round carries no answer: the same step and its error, nothing audited the person asks for a new code the person types the expired code again the person types another code the otp is not of the offered length the code has passed its 10 minutes: refused with code_expired, neither compared nor counted the code is live the identifier is inside a wait: refused, neither compared nor counted the attempt is reserved fails: the ladder store fails; nothing is compared start Six digits from a CSPRNG, and a salt of its own. The send caps of PRO-14 count the request before anything is kept, with the code after a correct first factor, so it skips the per-address ceiling and counts toward its own cap. An accepted code replaces the transaction's code, if any, by its salt and HMAC, and keeps the count of wrong answers; its mail is dispatched after the answer is built. A capped one keeps nothing new. Issue a code and ask the capped sender to mail it issuingCode · server step The step grammar has read it against the offered params: an otp of exactly the offered length, or none, which asks for a new code. An answer to another step is ignored, as every eager answer is. Take the round's answer to the email_code step takingAnswer · server step A new code may be asked for one minute after the last one, and the send caps still count it. Ask whether a new code is due checkingResend · server step The ladder hook of PRO-12 with the email-code counter, keyed on the identifier HMAC: one atomic operation reads the wait, refuses an attempt inside it, and otherwise counts the attempt as a failure until it settles. A store error refuses the round before the code is compared. Reserve the answer on the email-code ladder reservingCodeAttempt · server step The answer's HMAC under the code's salt against the held one, in constant time. The right code settles the reservation as a success, which clears the email-code counter and no other: never the password's. Compare the answer with the held code, in constant time comparingCode · server step The answer's HMAC differs from the held one: the reservation settles as a failure, and the transaction counts one wrong answer more. The 5th burns the code. Compare a wrong answer with the held code, in constant time comparingWrongCode · server step In one database transaction, the password credential's email_verified and the email attribute's verified flag, for the subject's enabled account only. The same person proved the password and the mailbox, so every credential stays. A store error refuses the round: the code stays live, and the same code may be answered again. Mark the address verified markingVerified · server step The params say why the code was sent, how many digits it has, how long it stays valid and when another may be asked for; the personal data is the address it went to, the transaction's identifier. Ask for the code offeringCode · server step The same step with invalid_credentials; the code stays, and so does the count of wrong answers. Ask for the code again codeRefused · server step The same step with code_expired: nothing was compared or counted. A new code is one resend away, once its minute has passed, which it has. Say the code has expired codeExpired · server step As takingAnswer, for an answer that is not the held code. Take a wrong answer to the email_code step takingWrongAnswer · server step As reservingCodeAttempt, for an answer that is not the held code. Reserve a wrong answer on the email-code ladder reservingWrongAttempt · server step The engine goes on with rule 8: a second factor the MFA policy requires and the account has (#653), else the sign-in completes. The completed sign-in's amr is the password's alone and its auth_time the password's. Go on with the sign-in, the address verified ends here The same 429 for every address. The transaction stays as far as the round took it: a code it held stays live. Refuse the round for the send caps ends here Neither compared nor counted; the transaction and its code stay. Refuse the answer inside the email-code ladder's wait ends here Five wrong answers in one transaction are its budget for codes. The transport discards the transaction; the Interaction API starts the sign-in again, and the email-code ladder counts on across transactions. Burn the code and end the transaction ends here The account was disabled or changed between the round's read and the write; nothing was written. End the transaction for an account that changed ends here A fault of the request, audited as invalid_answer; the transaction and its code stay. Refuse a malformed answer ends here The send caps, the ladder or the database cannot be reached: fail closed. The transaction stays as far as the round took it. Refuse the round while a store is down ends here

blue line: the main storyred dashed line: a failure path (shown with “Show failure paths”)dotted line: decided by a rule, nobody actsdashed grey box: the server decides, nothing is shown

What each step answers

StepThe outside world sees
Ask for the code
offeringCode
kind: step · body: {"current":{"step":"email_code","params":{"purpose":"verify_account","length":6,"expires_in":"the seconds the code has left, at most 600","resend_after":"the seconds until another code may be asked for, at most 60"}},"alternatives":[],"personal":{"identifier":"the transaction's identifier"}}
Ask for the code again
codeRefused
kind: step · body: {"current":{"step":"email_code","params":{"purpose":"verify_account","length":6,"expires_in":"the seconds the code has left, at most 600","resend_after":"the seconds until another code may be asked for, at most 60"},"error":"invalid_credentials"},"alternatives":[],"personal":{"identifier":"the transaction's identifier"}}
Say the code has expired
codeExpired
kind: step · body: {"current":{"step":"email_code","params":{"purpose":"verify_account","length":6,"expires_in":600,"resend_after":0},"error":"code_expired"},"alternatives":[],"personal":{"identifier":"the transaction's identifier"}}
Go on with the sign-in, the address verified
verified
kind: continue · body: {"verified":"the transaction verified email_code with purpose verify_account; it adds no amr value","amr":"[\"pwd\"]","authTime":"when the password was verified, unchanged"}
Refuse the round for the send caps
sendCapped
kind: refused · body: {"reason":"throttled","retryAfterSeconds":"the send cap's"}
Refuse the answer inside the email-code ladder's wait
lockedOut
kind: refused · body: {"reason":"throttled","retryAfterSeconds":"the ladder's"}
Burn the code and end the transaction
burned
kind: refused · body: {"reason":"transaction_ended","transaction":null}
End the transaction for an account that changed
ended
kind: refused · body: {"reason":"transaction_ended","transaction":null}
Refuse a malformed answer
answerRefused
kind: refused · body: {"reason":"invalid_answer"}
Refuse the round while a store is down
unavailable
kind: refused · body: {"reason":"unavailable"}

How it decides what happens next

Take the round's answer to the email_code step

#IfThen
1the code has passed its 10 minutes: refused with code_expired, audited, neither compared nor countedSay the code has expired
2the code is liveReserve the answer on the email-code ladder

The first matching rule wins.

Exact conditions
#ConditionGoes toSituations
1expiredcodeExpired76 of 152
2otherwisereservingCodeAttempt76 of 152
expired
codeAge == 'expired'

Ask whether a new code is due

#IfThen
1less than a minute has passed: the same step, nothing mailed, and the floor is waited outAsk for the code
2a new code is due; the count of wrong answers staysIssue a code and ask the capped sender to mail it

The first matching rule wins.

Exact conditions
#ConditionGoes toSituations
1resendNotDueofferingCode80 of 160
2otherwiseissuingCode80 of 160
resendNotDue
not resendDue

Compare the answer with the held code, in constant time

#IfThen
1the code is right: the reservation settles as a successMark the address verified

The first matching rule wins.

Exact conditions
#ConditionGoes toSituations
1otherwisemarkingVerified38 of 38

Compare a wrong answer with the held code, in constant time

#IfThen
1the 5th wrong answer of the transaction: the code is burned, the transaction ends, and code_burned is audited under the subjectBurn the code and end the transaction
2the first wrong answer: refused with invalid_credentials, audited as invalid_codeAsk for the code again
3the second wrong answer: refused alikeAsk for the code again
4the third wrong answer: refused alikeAsk for the code again
5the fourth wrong answer: refused alike; the next wrong one burns the codeAsk for the code again

The first matching rule wins.

Exact conditions
#ConditionGoes toSituations
1wrongFifthburned8 of 38
2wrongZerocodeRefused6 of 38
3wrongOnecodeRefused8 of 38
4wrongTwocodeRefused8 of 38
5otherwisecodeRefused8 of 38
wrongFifth
wrongAnswers == 'four'
wrongZero
wrongAnswers == 'zero'
wrongOne
wrongAnswers == 'one'
wrongTwo
wrongAnswers == 'two'

Take a wrong answer to the email_code step

#IfThen
1the code has passed its 10 minutes: refused with code_expired, neither compared nor countedSay the code has expired
2the code is liveReserve a wrong answer on the email-code ladder

The first matching rule wins.

Exact conditions
#ConditionGoes toSituations
1expiredcodeExpired76 of 152
2otherwisereservingWrongAttempt76 of 152
expired
codeAge == 'expired'

Every way it can end

It ends withWhenWhat happens
Go on with the sign-in, the address verified
ends here
The send caps let the code through, or drop its mail quietly at the address-and-IP tier, the code was issued less than 10 minutes ago, the last code was issued less than a minute ago, the identifier is outside a wait of the email-code ladder, the account is the subject's, enabled, with its password credential
  1. Issue a code and ask the capped sender to mail it: the code is accepted: its HMAC is kept, its mail queued, and the floor is waited out
  2. Ask for the code: the person types the code from the mail
  3. Take the round's answer to the email_code step: the code is live
  4. Reserve the answer on the email-code ladder: the attempt is reserved
  5. Compare the answer with the held code, in constant time: the code is right: the reservation settles as a success
  6. Mark the address verified: the address is verified: the code is consumed, email_code joins the transaction's verified steps with no amr, and the step is audited as passed under the subject
Refuse the round for the send caps
ends here
A tier that answers 429 is full: the per-IP, the per-client or the after-first-factor cap, the code was issued less than 10 minutes ago, the last code was issued less than a minute ago, the identifier is outside a wait of the email-code ladder, the account is the subject's, enabled, with its password credential
  1. Issue a code and ask the capped sender to mail it: a tier that answers 429 is full: nothing is mailed, a code the transaction held stays, send_capped is audited, and the floor is waited out
Refuse the answer inside the email-code ladder's wait
ends here
The send caps let the code through, or drop its mail quietly at the address-and-IP tier, the code was issued less than 10 minutes ago, the last code was issued less than a minute ago, fewer than its wait has passed since the identifier counted 5 or more wrong codes in a row, the account is the subject's, enabled, with its password credential
  1. Issue a code and ask the capped sender to mail it: the code is accepted: its HMAC is kept, its mail queued, and the floor is waited out
  2. Ask for the code: the person types the code from the mail
  3. Take the round's answer to the email_code step: the code is live
  4. Reserve the answer on the email-code ladder: the identifier is inside a wait: refused, neither compared nor counted, audited as locked_out
Burn the code and end the transaction
ends here
The send caps let the code through, or drop its mail quietly at the address-and-IP tier, the code was issued less than 10 minutes ago, the last code was issued less than a minute ago, the identifier is outside a wait of the email-code ladder, the account is the subject's, enabled, with its password credential
  1. Issue a code and ask the capped sender to mail it: the code is accepted: its HMAC is kept, its mail queued, and the floor is waited out
  2. Ask for the code: the person types another code
  3. Take a wrong answer to the email_code step: the code is live
  4. Reserve a wrong answer on the email-code ladder: the attempt is reserved
  5. Compare a wrong answer with the held code, in constant time: the first wrong answer: refused with invalid_credentials, audited as invalid_code
  6. Ask for the code again: the person types another wrong code
  7. Take a wrong answer to the email_code step: the code is live
  8. Reserve a wrong answer on the email-code ladder: the attempt is reserved
  9. Compare a wrong answer with the held code, in constant time: the second wrong answer: refused alike
  10. Ask for the code again: the person types another wrong code
  11. Take a wrong answer to the email_code step: the code is live
  12. Reserve a wrong answer on the email-code ladder: the attempt is reserved
  13. Compare a wrong answer with the held code, in constant time: the third wrong answer: refused alike
  14. Ask for the code again: the person types another wrong code
  15. Take a wrong answer to the email_code step: the code is live
  16. Reserve a wrong answer on the email-code ladder: the attempt is reserved
  17. Compare a wrong answer with the held code, in constant time: the fourth wrong answer: refused alike; the next wrong one burns the code
  18. Ask for the code again: the person types another wrong code
  19. Take a wrong answer to the email_code step: the code is live
  20. Reserve a wrong answer on the email-code ladder: the attempt is reserved
  21. Compare a wrong answer with the held code, in constant time: the 5th wrong answer of the transaction: the code is burned, the transaction ends, and code_burned is audited under the subject
End the transaction for an account that changed
ends here
The send caps let the code through, or drop its mail quietly at the address-and-IP tier, the code was issued less than 10 minutes ago, the last code was issued less than a minute ago, the identifier is outside a wait of the email-code ladder, the account was disabled or lost its password credential since the round read it
  1. Issue a code and ask the capped sender to mail it: the code is accepted: its HMAC is kept, its mail queued, and the floor is waited out
  2. Ask for the code: the person types the code from the mail
  3. Take the round's answer to the email_code step: the code is live
  4. Reserve the answer on the email-code ladder: the attempt is reserved
  5. Compare the answer with the held code, in constant time: the code is right: the reservation settles as a success
  6. Mark the address verified: the account was disabled or changed since the read: nothing is written, and the transaction ends
Refuse a malformed answer
ends here
The send caps let the code through, or drop its mail quietly at the address-and-IP tier, the code was issued less than 10 minutes ago, the last code was issued less than a minute ago, the identifier is outside a wait of the email-code ladder, the account is the subject's, enabled, with its password credential
  1. Issue a code and ask the capped sender to mail it: the code is accepted: its HMAC is kept, its mail queued, and the floor is waited out
  2. Ask for the code: the otp is not of the offered length: refused as invalid_answer, nothing compared or counted
Refuse the round while a store is down
ends here
The send caps let the code through, or drop its mail quietly at the address-and-IP tier, the code was issued less than 10 minutes ago, the last code was issued less than a minute ago, the identifier is outside a wait of the email-code ladder, the account is the subject's, enabled, with its password credential
  1. Issue a code and ask the capped sender to mail it: it fails, because the send-cap store fails: nothing is mailed or kept; the floor is waited out

Each row is also a generated test: set the starting situation, send the events, expect the ending.

What is guaranteed

These hold in all 1862 situations the model can reach, from 32 different starting situations.

Exact rules
IdNever
no-verification-inside-a-waitstate == 'verified' and ladder == 'inWait'
no-verification-after-ten-minutesstate == 'verified' and codeAge == 'expired'
reserve-before-comparecodeCompared and not attemptReserved
refusals-compare-nothingstate in ['codeExpired', 'lockedOut', 'sendCapped', 'answerRefused'] and codeCompared
the-fifth-wrong-answer-burnswrongAnswers == 'five' and state != 'burned'
a-resend-never-resets-the-countansweredWrong and wrongAnswers == 'zero'
nothing-mailed-when-cappedstate == 'sendCapped' and mailQueued
a-code-is-never-a-factorfactorClaimed
the-address-is-verified-only-by-its-codeaddressVerified and state != 'verified'
a-consumed-code-is-gonestate == 'verified' and codeHeld
every-refusal-is-auditedstate in ['codeRefused', 'lockedOut', 'sendCapped', 'burned', 'ended', 'answerRefused'] and not audited
every-round-waits-the-floorstate in ['offeringCode', 'codeRefused', 'codeExpired', 'sendCapped', 'lockedOut', 'burned', 'ended', 'answerRefused', 'unavailable'] and not floorWaited

Reference

All transitions (39)
FromEventGuardToEffectsTaken
issuingCodeDONEcappedsendCappedaudit waitFlooryes
issuingCodeDONEofferingCodeholdCode queueMail waitFlooryes
issuingCodeFAIL.storeunavailablewaitFlooryes
takingAnswerrule 1expiredcodeExpiredaudit waitFlooryes
takingAnswerrule 2reservingCodeAttemptyes
checkingResendrule 1resendNotDueofferingCodewaitFlooryes
checkingResendrule 2issuingCodeyes
reservingCodeAttemptDONEinWaitlockedOutaudit waitFlooryes
reservingCodeAttemptDONEcomparingCodereserveAttemptyes
reservingCodeAttemptFAIL.storeunavailablewaitFlooryes
comparingCoderule 1markingVerifiedcompareCodeyes
comparingWrongCoderule 1wrongFifthburnedcompareCode countFifth audit waitFlooryes
comparingWrongCoderule 2wrongZerocodeRefusedcompareCode countFirst audit waitFlooryes
comparingWrongCoderule 3wrongOnecodeRefusedcompareCode countSecond audit waitFlooryes
comparingWrongCoderule 4wrongTwocodeRefusedcompareCode countThird audit waitFlooryes
comparingWrongCoderule 5codeRefusedcompareCode countFourth audit waitFlooryes
markingVerifiedDONEchangedendedaudit waitFlooryes
markingVerifiedDONEverifiedconsumeCode markVerified audityes
markingVerifiedFAIL.storeunavailablewaitFlooryes
offeringCodeROUNDofferingCodenewRound waitFlooryes
offeringCodeRIGHT_CODEtakingAnswernewRoundyes
offeringCodeWRONG_CODEtakingWrongAnswernewRoundyes
offeringCodeRESENDcheckingResendnewRound moveOnyes
offeringCodeMALFORMEDanswerRefusednewRound audit waitFlooryes
codeRefusedROUNDofferingCodenewRound waitFlooryes
codeRefusedRIGHT_CODEtakingAnswernewRound moveOnyes
codeRefusedWRONG_CODEtakingWrongAnswernewRound moveOnyes
codeRefusedRESENDcheckingResendnewRound moveOnyes
codeRefusedMALFORMEDanswerRefusednewRound audit waitFlooryes
codeExpiredROUNDcodeExpirednewRound waitFlooryes
codeExpiredRESENDissuingCodenewRound moveOnyes
codeExpiredRIGHT_CODEtakingAnswernewRoundyes
codeExpiredWRONG_CODEtakingWrongAnswernewRoundyes
codeExpiredMALFORMEDanswerRefusednewRound audit waitFlooryes
takingWrongAnswerrule 1expiredcodeExpiredaudit waitFlooryes
takingWrongAnswerrule 2reservingWrongAttemptyes
reservingWrongAttemptDONEinWaitlockedOutaudit waitFlooryes
reservingWrongAttemptDONEcomparingWrongCodereserveAttemptyes
reservingWrongAttemptFAIL.storeunavailablewaitFlooryes