proposed core.recovery-code

Redeem a recovery code in place of a lost factor, and show a new set once

After a correct first factor, rule 8 asks for a factor the account holds. On the hosted pages a recovery code is offered beside it, or alone when the transport can serve no step of that factor. A right code, answered outside the account's recovery-code ladder's wait, is marked used, the user is told, and the sign-in goes on with no amr added; under an MFA policy the person must bind a new second factor first. A wrong one is refused and counted. A transport that serves no recovery step (a FiPA client) is sent to the hosted pages where a code is the only way on. Before a sign-in that proved a factor beyond the password completes, an account that holds no set gets one, shown once until the person confirms they saved it.

What this view shows

the recovery_code step from its offer after a first factor to the code taken or refused, round by round, and the recovery_codes step from a new set to the person's confirmation.

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 Interaction API, behind the hosted pages, rule 8 asks for the strongest bound factor, a second factor or the factor beyond the password, no registered step of that factor, or none the transport serves, the account holds at least one unused code, the account is outside a wait of its recovery-code ladder, an unused code of the set, not taken by another round since, fewer than 100 failures in a row, this one included, mfaRequired is false, the account is the subject's and enabled, the account holds a set the person confirmed, or holds no factor beyond the password, or this transaction proved none.

  1. Ask whether rule 8 owes a factor: a factor is owed
  2. Read how many unused codes the account holds: the hosted pages, and an unused code to redeem
  3. Offer a recovery code: the person types a recovery code
  4. Reserve the answer on the recovery-code ladder: the attempt is reserved; it spends the bot challenge passed for it
  5. Check the answer against the set, and take it once: the code is taken: recovery_code joins the transaction's verified steps with no amr, auth_time stays, the step is audited as passed under the subject, and "a recovery code was used" is mailed
  6. Read the account again: the account is as it was
  7. Ask whether the code met what rule 8 owed: nothing more is owed for the factor
  8. Ask whether a set is owed before the sign-in completes: no set is owed: the sign-in completes

Result: Complete the sign-in.

The flow

a factor is owed every factor rule 8 asks for is proved a FiPA client: the factor's own step when it can serve one, else a redirect, which it answers with redirect_to_web the account holds no unused code: the factor alone, or a redirect when no step of it can be served the hosted pages, and an unused code to redeem the account is inside a wait: refused, neither checked nor counted, audited as locked_out the attempt is reserved; it spends the bot challenge passed for it the code is taken: recovery_code joins the transaction's verified steps with no amr, auth_time stays, the step is audited as passed under the subject, and "a recovery code was used" is mailed a wrong or used code, counted at or past the ceiling of 100: refused like any other, and the user (at most once a day) and the operator are told a wrong or used code: refused with invalid_credentials, counted, audited as invalid_credentials the account was disabled or removed since: the transaction ends the account is as it was the MFA policy requires a second factor the code does not make: the set-up step, which no transport can serve until #651 and #653 add one, so a redirect nothing more is owed for the factor no set is owed: the sign-in completes a FiPA client cannot show a set: a redirect, answered with redirect_to_web a set is owed, and the hosted pages show it the set is written; its codes are shown another sign-in of the account showed a set and the person confirmed it meanwhile: that set stays, nothing is written, and nothing is owed the next round carries no answer: the same offer the person types a recovery code the answer is not shaped like a code: refused as invalid_answer, nothing checked or counted the person answers the factor's own step instead the next round carries no answer the person types another code the answer is not shaped like a code the person answers the factor's own step instead the person confirms they saved the codes: the store marks the set this transaction showed confirmed, named by its id, and recovery_codes joins the verified steps with no amr the person confirms, but another sign-in or a regeneration replaced the set shown meanwhile: those codes no longer work, nothing is confirmed, and a new set is issued in their place the next round brings no confirmation: the shown codes are gone, so a new set replaces them the confirmation is not `true`: refused as invalid_answer start A first factor proved the account, and the account was read again with every factor it holds, a disabled one included. Rule 8 owes a factor while the transaction has not proved the strongest bound one (the downgrade rule), while the MFA policy requires mfa it has not made, or while a forced update needs a factor beyond the password it has not proved. A redeemed code meets the first and the last, never the MFA policy. Ask whether rule 8 owes a factor afterFirstFactor · server step Only a transport that serves recovery_code reads them: the Interaction API. A FiPA client never gets the step, so the read and the offer are the hosted pages' alone. Read how many unused codes the account holds readingCodes · server step The limiters' credential round first (#636), then the ladder hook of PRO-12 with the recovery_code counter, keyed on the realm and the user: one atomic operation reads the wait, refuses an attempt inside it, and otherwise counts the attempt as a failure until it settles. No end-user IP is known to it: the ladder has no known-IP exemption. A store error refuses the round before any code is checked. Reserve the answer on the recovery-code ladder reservingAttempt · server step The answer, with case, spaces and dashes ignored, is checked against every unused hash of the set with the password hasher, padded to 10 with a decoy; an answer that cannot be a code is checked against the decoy alone, as long. A match is marked used in one write that only an unused code passes, with its security-log event, so two rounds with the same code take it once. A taken code settles the reservation as a success, which clears the recovery-code counter and no other. Check the answer against the set, and take it once checkingCode · server step The account the code was taken for, every factor included, as after any factor of rule 8. Read the account again readingAccountAgain · server step A code meets the downgrade rule and the forced update's factor beyond the password. It never makes mfa: under an MFA policy that requires a second factor, the person binds a new one before the sign-in completes. Ask whether the code met what rule 8 owed afterRedemption · server step The first enrolment of a second factor binds it within a transaction that proves it (#651, #653): an account that holds a factor beyond the password, after a transaction that proved one, and no set gets one now. A transaction that took a recovery code holds a set already. Ask whether a set is owed before the sign-in completes checkingSet · server step Ten codes of 80 bits from a CSPRNG; their Argon2id hashes replace the account's set, if any, in one write, unless the account holds a set the person confirmed by now. The codes go to this round's answer alone, as the step's personal data, and the transaction keeps only the new set's id, which waits to be confirmed. Issue a new set issuingSet · server step The factor's own steps first, when the transport serves one, and recovery_code last; recovery_code alone when it serves none. The personal data is the transaction's identifier. Offer a recovery code offeringRecoveryCode · server step The same offer, recovery_code current with invalid_credentials; nothing says how many codes are left. Ask for a recovery code again codeRefused · server step The recovery_codes step with the set's 10 codes as its personal data. Nothing keeps them: a round that brings no confirmation gets a new set, which replaces this one. Show the new set once offeringSet · server step Rule 9, after whatever rule 8 still asks for (the inline verification, the forced update). The amr is the proved factors' alone: a taken code adds none, and auth_time is the last factor's. Complete the sign-in ends here A step the transport cannot serve: a set to show on a FiPA client, or a new second factor to bind under an MFA policy. The FiPA endpoint answers redirect_to_web; the Interaction API ends the interaction. Send the person to a page that can go on ends here No recovery code: the transport serves none, or the account holds no unused one. The factor's own steps, or a redirect when no step of the factor can be served, drawn by flows/core/sign-in. Offer the factor alone ends here The person answered the factor's step offered beside the code, drawn with that step (#651, Go on with the factor's own answer ends here Neither checked nor counted; the transaction stays, and so does the offer. Refuse the answer inside the recovery-code ladder's wait ends here The account was disabled or removed after its code was taken; the code stays used. End the transaction for an account that changed ends here A fault of the request, audited as invalid_answer; the transaction stays. Refuse a malformed answer ends here
a factor is owed every factor rule 8 asks for is proved a FiPA client: the factor's own step when it can serve one, else a redirect, which it answers with redirect_to_web the account holds no unused code: the factor alone, or a redirect when no step of it can be served the hosted pages, and an unused code to redeem fails: the database fails; nothing is offered the account is inside a wait: refused, neither checked nor counted, audited as locked_out the attempt is reserved; it spends the bot challenge passed for it fails: the ladder store fails; nothing is checked the code is taken: recovery_code joins the transaction's verified steps with no amr, auth_time stays, the step is audited as passed under the subject, and "a recovery code was used" is mailed a wrong or used code, counted at or past the ceiling of 100: refused like any other, and the user (at most once a day) and the operator are told a wrong or used code: refused with invalid_credentials, counted, audited as invalid_credentials fails: the database fails: no code is taken, and the attempt stays counted the account was disabled or removed since: the transaction ends the account is as it was fails: the database fails; the code stays taken the MFA policy requires a second factor the code does not make: the set-up step, which no transport can serve until #651 and #653 add one, so a redirect nothing more is owed for the factor no set is owed: the sign-in completes a FiPA client cannot show a set: a redirect, answered with redirect_to_web a set is owed, and the hosted pages show it the set is written; its codes are shown another sign-in of the account showed a set and the person confirmed it meanwhile: that set stays, nothing is written, and nothing is owed fails: the database fails: no set is written the next round carries no answer: the same offer the person types a recovery code the answer is not shaped like a code: refused as invalid_answer, nothing checked or counted the person answers the factor's own step instead the next round carries no answer the person types another code the answer is not shaped like a code the person answers the factor's own step instead the person confirms they saved the codes: the store marks the set this transaction showed confirmed, named by its id, and recovery_codes joins the verified steps with no amr the person confirms, but another sign-in or a regeneration replaced the set shown meanwhile: those codes no longer work, nothing is confirmed, and a new set is issued in their place the next round brings no confirmation: the shown codes are gone, so a new set replaces them the confirmation is not `true`: refused as invalid_answer start A first factor proved the account, and the account was read again with every factor it holds, a disabled one included. Rule 8 owes a factor while the transaction has not proved the strongest bound one (the downgrade rule), while the MFA policy requires mfa it has not made, or while a forced update needs a factor beyond the password it has not proved. A redeemed code meets the first and the last, never the MFA policy. Ask whether rule 8 owes a factor afterFirstFactor · server step Only a transport that serves recovery_code reads them: the Interaction API. A FiPA client never gets the step, so the read and the offer are the hosted pages' alone. Read how many unused codes the account holds readingCodes · server step The limiters' credential round first (#636), then the ladder hook of PRO-12 with the recovery_code counter, keyed on the realm and the user: one atomic operation reads the wait, refuses an attempt inside it, and otherwise counts the attempt as a failure until it settles. No end-user IP is known to it: the ladder has no known-IP exemption. A store error refuses the round before any code is checked. Reserve the answer on the recovery-code ladder reservingAttempt · server step The answer, with case, spaces and dashes ignored, is checked against every unused hash of the set with the password hasher, padded to 10 with a decoy; an answer that cannot be a code is checked against the decoy alone, as long. A match is marked used in one write that only an unused code passes, with its security-log event, so two rounds with the same code take it once. A taken code settles the reservation as a success, which clears the recovery-code counter and no other. Check the answer against the set, and take it once checkingCode · server step The account the code was taken for, every factor included, as after any factor of rule 8. Read the account again readingAccountAgain · server step A code meets the downgrade rule and the forced update's factor beyond the password. It never makes mfa: under an MFA policy that requires a second factor, the person binds a new one before the sign-in completes. Ask whether the code met what rule 8 owed afterRedemption · server step The first enrolment of a second factor binds it within a transaction that proves it (#651, #653): an account that holds a factor beyond the password, after a transaction that proved one, and no set gets one now. A transaction that took a recovery code holds a set already. Ask whether a set is owed before the sign-in completes checkingSet · server step Ten codes of 80 bits from a CSPRNG; their Argon2id hashes replace the account's set, if any, in one write, unless the account holds a set the person confirmed by now. The codes go to this round's answer alone, as the step's personal data, and the transaction keeps only the new set's id, which waits to be confirmed. Issue a new set issuingSet · server step The factor's own steps first, when the transport serves one, and recovery_code last; recovery_code alone when it serves none. The personal data is the transaction's identifier. Offer a recovery code offeringRecoveryCode · server step The same offer, recovery_code current with invalid_credentials; nothing says how many codes are left. Ask for a recovery code again codeRefused · server step The recovery_codes step with the set's 10 codes as its personal data. Nothing keeps them: a round that brings no confirmation gets a new set, which replaces this one. Show the new set once offeringSet · server step Rule 9, after whatever rule 8 still asks for (the inline verification, the forced update). The amr is the proved factors' alone: a taken code adds none, and auth_time is the last factor's. Complete the sign-in ends here A step the transport cannot serve: a set to show on a FiPA client, or a new second factor to bind under an MFA policy. The FiPA endpoint answers redirect_to_web; the Interaction API ends the interaction. Send the person to a page that can go on ends here No recovery code: the transport serves none, or the account holds no unused one. The factor's own steps, or a redirect when no step of the factor can be served, drawn by flows/core/sign-in. Offer the factor alone ends here The person answered the factor's step offered beside the code, drawn with that step (#651, Go on with the factor's own answer ends here Neither checked nor counted; the transaction stays, and so does the offer. Refuse the answer inside the recovery-code ladder's wait ends here The account was disabled or removed after its code was taken; the code stays used. End the transaction for an account that changed ends here A fault of the request, audited as invalid_answer; the transaction stays. Refuse a malformed answer ends here The database or the ladder 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
Offer a recovery code
offeringRecoveryCode
kind: step · body: {"current":"the factor's first step, else recovery_code","alternatives":"the factor's other steps, then recovery_code","personal":{"identifier":"the transaction's identifier"}}
Ask for a recovery code again
codeRefused
kind: step · body: {"current":{"step":"recovery_code","params":{},"error":"invalid_credentials"},"alternatives":"the factor's steps the transport serves","personal":{"identifier":"the transaction's identifier"}}
Show the new set once
offeringSet
kind: step · body: {"current":{"step":"recovery_codes","params":{"count":10}},"alternatives":[],"personal":{"codes":"the 10 codes, in four groups of four base32 characters"}}
Complete the sign-in
completed
kind: complete · body: {"amr":"the proved factors' values; nothing for recovery_code or recovery_codes, and never mfa from a code","authTime":"the last factor's, never the code's"}
Send the person to a page that can go on
redirected
kind: redirect · body: {"reason":"step_not_available","transaction":null}
Offer the factor alone
factorOffered
kind: step · body: {"current":"the factor's first step, or a redirect with step_not_available"}
Go on with the factor's own answer
factorAnswered
kind: continue · body: {"verified":"the factor's step, as its handler verifies it"}
Refuse the answer inside the recovery-code ladder's wait
lockedOut
kind: refused · body: {"reason":"throttled","retryAfterSeconds":"the ladder's"}
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

Ask whether rule 8 owes a factor

#IfThen
1a factor is owedRead how many unused codes the account holds
2every factor rule 8 asks for is provedAsk whether a set is owed before the sign-in completes

The first matching rule wins.

Exact conditions
#ConditionGoes toSituations
1owedreadingCodes768 of 1536
2otherwisecheckingSet768 of 1536
owed
factorOwed

Ask whether the code met what rule 8 owed

#IfThen
1the MFA policy requires a second factor the code does not make: the set-up step, which no transport can serve until #651 and #653 add one, so a redirectSend the person to a page that can go on
2nothing more is owed for the factorAsk whether a set is owed before the sign-in completes

The first matching rule wins.

Exact conditions
#ConditionGoes toSituations
1mfaredirected16 of 32
2otherwisecheckingSet16 of 32
mfa
mfaRequired

Ask whether a set is owed before the sign-in completes

#IfThen
1no set is owed: the sign-in completesComplete the sign-in
2a FiPA client cannot show a set: a redirect, answered with redirect_to_webSend the person to a page that can go on
3a set is owed, and the hosted pages show itIssue a new set

The first matching rule wins.

Exact conditions
#ConditionGoes toSituations
1noSetOwedcompleted400 of 784
2notHostedredirected192 of 784
3otherwiseissuingSet192 of 784
notHosted
not hostedPages
noSetOwed
not setOwed or codeTaken

Every way it can end

It ends withWhenWhat happens
Complete the sign-in
ends here
A FiPA client, which may not serve recovery_code or recovery_codes, every factor rule 8 asks for is proved, no registered step of that factor, or none the transport serves, the account holds no set, or every code of it is used, the account is outside a wait of its recovery-code ladder, an unused code of the set, not taken by another round since, fewer than 100 failures in a row, this one included, mfaRequired is false, the account is the subject's and enabled, the account holds a set the person confirmed, or holds no factor beyond the password, or this transaction proved none
  1. Ask whether rule 8 owes a factor: every factor rule 8 asks for is proved
  2. Ask whether a set is owed before the sign-in completes: no set is owed: the sign-in completes
Send the person to a page that can go on
ends here
A FiPA client, which may not serve recovery_code or recovery_codes, every factor rule 8 asks for is proved, no registered step of that factor, or none the transport serves, the account holds no set, or every code of it is used, the account is outside a wait of its recovery-code ladder, an unused code of the set, not taken by another round since, fewer than 100 failures in a row, this one included, mfaRequired is false, the account is the subject's and enabled, the account holds a factor beyond the password, this transaction proved one, and the account holds no set the person confirmed: none, one this transaction issued and showed, or one nobody confirmed (a closed tab, a lost answer), which a new set replaces
  1. Ask whether rule 8 owes a factor: every factor rule 8 asks for is proved
  2. Ask whether a set is owed before the sign-in completes: a FiPA client cannot show a set: a redirect, answered with redirect_to_web
Offer the factor alone
ends here
A FiPA client, which may not serve recovery_code or recovery_codes, rule 8 asks for the strongest bound factor, a second factor or the factor beyond the password, no registered step of that factor, or none the transport serves, the account holds no set, or every code of it is used, the account is outside a wait of its recovery-code ladder, an unused code of the set, not taken by another round since, fewer than 100 failures in a row, this one included, mfaRequired is false, the account is the subject's and enabled, the account holds a set the person confirmed, or holds no factor beyond the password, or this transaction proved none
  1. Ask whether rule 8 owes a factor: a factor is owed
  2. Read how many unused codes the account holds: a FiPA client: the factor's own step when it can serve one, else a redirect, which it answers with redirect_to_web
Go on with the factor's own answer
ends here
The Interaction API, behind the hosted pages, rule 8 asks for the strongest bound factor, a second factor or the factor beyond the password, no registered step of that factor, or none the transport serves, the account holds at least one unused code, the account is outside a wait of its recovery-code ladder, an unused code of the set, not taken by another round since, fewer than 100 failures in a row, this one included, mfaRequired is false, the account is the subject's and enabled, the account holds a set the person confirmed, or holds no factor beyond the password, or this transaction proved none
  1. Ask whether rule 8 owes a factor: a factor is owed
  2. Read how many unused codes the account holds: the hosted pages, and an unused code to redeem
  3. Offer a recovery code: the person answers the factor's own step instead
Refuse the answer inside the recovery-code ladder's wait
ends here
The Interaction API, behind the hosted pages, rule 8 asks for the strongest bound factor, a second factor or the factor beyond the password, no registered step of that factor, or none the transport serves, the account holds at least one unused code, the account is outside a wait of its recovery-code ladder, an unused code of the set, not taken by another round since, fewer than 100 failures in a row, this one included, mfaRequired is false, the account is the subject's and enabled, the account holds a set the person confirmed, or holds no factor beyond the password, or this transaction proved none
  1. Ask whether rule 8 owes a factor: a factor is owed
  2. Read how many unused codes the account holds: the hosted pages, and an unused code to redeem
  3. Offer a recovery code: the person types a recovery code
  4. Reserve the answer on the recovery-code ladder: the account is inside a wait: refused, neither checked nor counted, audited as locked_out
End the transaction for an account that changed
ends here
The Interaction API, behind the hosted pages, rule 8 asks for the strongest bound factor, a second factor or the factor beyond the password, no registered step of that factor, or none the transport serves, the account holds at least one unused code, the account is outside a wait of its recovery-code ladder, an unused code of the set, not taken by another round since, fewer than 100 failures in a row, this one included, mfaRequired is false, the account was disabled or removed since the round read it, the account holds a set the person confirmed, or holds no factor beyond the password, or this transaction proved none
  1. Ask whether rule 8 owes a factor: a factor is owed
  2. Read how many unused codes the account holds: the hosted pages, and an unused code to redeem
  3. Offer a recovery code: the person types a recovery code
  4. Reserve the answer on the recovery-code ladder: the attempt is reserved; it spends the bot challenge passed for it
  5. Check the answer against the set, and take it once: the code is taken: recovery_code joins the transaction's verified steps with no amr, auth_time stays, the step is audited as passed under the subject, and "a recovery code was used" is mailed
  6. Read the account again: the account was disabled or removed since: the transaction ends
Refuse a malformed answer
ends here
The Interaction API, behind the hosted pages, rule 8 asks for the strongest bound factor, a second factor or the factor beyond the password, no registered step of that factor, or none the transport serves, the account holds at least one unused code, the account is outside a wait of its recovery-code ladder, an unused code of the set, not taken by another round since, fewer than 100 failures in a row, this one included, mfaRequired is false, the account is the subject's and enabled, the account holds a set the person confirmed, or holds no factor beyond the password, or this transaction proved none
  1. Ask whether rule 8 owes a factor: a factor is owed
  2. Read how many unused codes the account holds: the hosted pages, and an unused code to redeem
  3. Offer a recovery code: the answer is not shaped like a code: refused as invalid_answer, nothing checked or counted
Refuse the round while a store is down
ends here
A FiPA client, which may not serve recovery_code or recovery_codes, rule 8 asks for the strongest bound factor, a second factor or the factor beyond the password, no registered step of that factor, or none the transport serves, the account holds no set, or every code of it is used, the account is outside a wait of its recovery-code ladder, an unused code of the set, not taken by another round since, fewer than 100 failures in a row, this one included, mfaRequired is false, the account is the subject's and enabled, the account holds a set the person confirmed, or holds no factor beyond the password, or this transaction proved none
  1. Ask whether rule 8 owes a factor: a factor is owed
  2. Read how many unused codes the account holds: it fails, because the database fails; nothing is offered

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

What is guaranteed

These hold in all 8976 situations the model can reach, from 1536 different starting situations.

Exact rules
IdNever
reserve-before-checkcodeChecked and not attemptReserved
nothing-checked-inside-a-waitstate == 'lockedOut' and codeChecked
only-an-unused-code-is-takencodeTaken and answer != 'unused'
the-hosted-pages-alone-redeemcodeChecked and not hostedPages
every-taken-code-is-toldcodeTaken and not usedNoticeQueued
the-ceiling-is-toldstate == 'codeRefused' and ceiling == 'reached' and not ceilingNoticed
a-code-is-never-a-factorfactorClaimed
a-code-never-meets-an-mfa-policystate == 'completed' and codeTaken and mfaRequired
a-code-is-never-keptcodeKept
a-new-set-is-confirmedstate == 'completed' and setIssued and not setConfirmed
every-refusal-is-auditedstate in ['codeRefused', 'lockedOut', 'ended', 'answerRefused'] and not audited
every-round-waits-the-floorstate in ['offeringRecoveryCode', 'codeRefused', 'offeringSet', 'completed', 'redirected', 'factorOffered', 'lockedOut', 'ended', 'answerRefused', 'unavailable'] and not floorWaited

Reference

All transitions (36)
FromEventGuardToEffectsTaken
afterFirstFactorrule 1owedreadingCodesyes
afterFirstFactorrule 2checkingSetyes
readingCodesDONEnotHostedfactorOfferedwaitFlooryes
readingCodesDONEnoCodesfactorOfferedwaitFlooryes
readingCodesDONEofferingRecoveryCodewaitFlooryes
readingCodesFAIL.storeunavailablewaitFlooryes
reservingAttemptDONEinWaitlockedOutaudit waitFlooryes
reservingAttemptDONEcheckingCodereserveAttemptyes
reservingAttemptFAIL.storeunavailablewaitFlooryes
checkingCodeDONEunusedCodereadingAccountAgaincheckCode takeCode audityes
checkingCodeDONEatCeilingcodeRefusedcheckCode noticeCeiling audit waitFlooryes
checkingCodeDONEcodeRefusedcheckCode audit waitFlooryes
checkingCodeFAIL.storeunavailablewaitFlooryes
readingAccountAgainDONEchangedendedaudit waitFlooryes
readingAccountAgainDONEafterRedemptionyes
readingAccountAgainFAIL.storeunavailablewaitFlooryes
afterRedemptionrule 1mfaredirectedwaitFlooryes
afterRedemptionrule 2checkingSetyes
checkingSetrule 1noSetOwedcompletedwaitFlooryes
checkingSetrule 2notHostedredirectedwaitFlooryes
checkingSetrule 3issuingSetyes
issuingSetDONEofferingSetissueSet waitFlooryes
issuingSetKEPTcompletedkeepConfirmedSet waitFlooryes
issuingSetFAIL.storeunavailablewaitFlooryes
offeringRecoveryCodeROUNDofferingRecoveryCodenewRound waitFlooryes
offeringRecoveryCodeCODEreservingAttemptnewRound moveOnyes
offeringRecoveryCodeMALFORMEDanswerRefusednewRound audit waitFlooryes
offeringRecoveryCodeFACTORfactorAnswerednewRoundyes
codeRefusedROUNDofferingRecoveryCodenewRound waitFlooryes
codeRefusedCODEreservingAttemptnewRound moveOnyes
codeRefusedMALFORMEDanswerRefusednewRound audit waitFlooryes
codeRefusedFACTORfactorAnswerednewRoundyes
offeringSetCONFIRMcompletednewRound confirmSet audit waitFlooryes
offeringSetREPLACEDissuingSetnewRoundyes
offeringSetROUNDissuingSetnewRoundyes
offeringSetMALFORMEDanswerRefusednewRound audit waitFlooryes