diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 945c42f73..d1eff990c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -124,6 +124,9 @@ jobs: - name: check run: make docs-check + - name: check generated FSM diagrams + run: make fsm-check + ######################## # run unit-test sqlite3 race ######################## diff --git a/Makefile b/Makefile index a4a12d1bc..39c567726 100644 --- a/Makefile +++ b/Makefile @@ -194,4 +194,20 @@ docs-check: docs fsm: @$(call print, "Generating state machine docs") ./scripts/fsm-generate.sh; -.PHONY: fsm + +FSM_FILES := \ + fsm/example_fsm.md \ + instantout/fsm.md \ + instantout/reservation/fsm.md \ + staticaddr/deposit/fsm.md \ + staticaddr/loopin/fsm.md + +fsm-check: fsm + @$(call print, "Verifying generated state machine docs") + if test -n "$$(git status --porcelain -- $(FSM_FILES))"; then \ + echo "Generated FSM diagrams are not up-to-date!"; \ + git status --porcelain -- $(FSM_FILES); \ + git diff -- $(FSM_FILES); \ + exit 1; \ + fi +.PHONY: fsm fsm-check diff --git a/fsm/example_fsm.md b/fsm/example_fsm.md index 882d3404b..7de064450 100644 --- a/fsm/example_fsm.md +++ b/fsm/example_fsm.md @@ -2,11 +2,11 @@ stateDiagram-v2 [*] --> InitFSM: OnRequestStuff InitFSM -InitFSM --> StuffSentOut: OnStuffSentOut InitFSM --> StuffFailed: OnError +InitFSM --> StuffSentOut: OnStuffSentOut StuffFailed StuffSentOut -StuffSentOut --> StuffSuccess: OnStuffSuccess StuffSentOut --> StuffFailed: OnError +StuffSentOut --> StuffSuccess: OnStuffSuccess StuffSuccess ``` \ No newline at end of file diff --git a/fsm/stateparser/stateparser.go b/fsm/stateparser/stateparser.go index cafedd7d3..edbadb109 100644 --- a/fsm/stateparser/stateparser.go +++ b/fsm/stateparser/stateparser.go @@ -12,8 +12,12 @@ import ( "github.com/lightninglabs/loop/fsm" "github.com/lightninglabs/loop/instantout" "github.com/lightninglabs/loop/instantout/reservation" + "github.com/lightninglabs/loop/staticaddr/deposit" + "github.com/lightninglabs/loop/staticaddr/loopin" ) +var errInvalidFSMSelector = errors.New("missing or unknown fsm selector") + func main() { if err := run(); err != nil { fmt.Println(err) @@ -35,35 +39,43 @@ func run() error { return err } - switch *stateMachine { + states, err := getStates(*stateMachine) + if err != nil { + return err + } + + return writeMermaidFile(fp, states) +} + +func getStates(stateMachine string) (fsm.States, error) { + switch stateMachine { case "example": exampleFSM := &fsm.ExampleFSM{} - err = writeMermaidFile(fp, exampleFSM.GetStates()) - if err != nil { - return err - } + return exampleFSM.GetStates(), nil case "reservation": reservationFSM := &reservation.FSM{} - err = writeMermaidFile(fp, reservationFSM.GetServerInitiatedReservationStates()) - if err != nil { - return err - } + return reservationFSM.GetServerInitiatedReservationStates(), nil case "instantout": - instantout := &instantout.FSM{} - err = writeMermaidFile(fp, instantout.GetV1ReservationStates()) - if err != nil { - return err - } + instantOutFSM := &instantout.FSM{} + return instantOutFSM.GetV1ReservationStates(), nil + + case "staticaddr-deposit": + depositFSM := &deposit.FSM{} + return depositFSM.DepositStatesV0(), nil + + case "staticaddr-loopin": + loopInFSM := &loopin.FSM{} + return loopInFSM.LoopInStatesV0(), nil default: - fmt.Println("Missing or wrong argument: fsm must be one of:") - fmt.Println("\treservations") - fmt.Println("\texample") + return nil, fmt.Errorf( + "%w %q; supported selectors: example, instantout, "+ + "reservation, staticaddr-deposit, staticaddr-loopin", + errInvalidFSMSelector, stateMachine, + ) } - - return nil } func writeMermaidFile(filename string, states fsm.States) error { @@ -86,8 +98,11 @@ func writeMermaidFile(filename string, states fsm.States) error { state = "[*]" } // write transitions - for edge, target := range edges.Transitions { - fmt.Fprintf(&b, "%s --> %s: %s\n", state, target, edge) + for _, edge := range sortedTransitionKeys(edges.Transitions) { + fmt.Fprintf( + &b, "%s --> %s: %s\n", state, + edges.Transitions[fsm.EventType(edge)], edge, + ) } } @@ -110,3 +125,14 @@ func sortedKeys(m fsm.States) []string { sort.Strings(keys) return keys } + +func sortedTransitionKeys(m fsm.Transitions) []string { + keys := make([]string, len(m)) + i := 0 + for k := range m { + keys[i] = string(k) + i++ + } + sort.Strings(keys) + return keys +} diff --git a/instantout/fsm.md b/instantout/fsm.md index 4b90fb275..a709aa682 100644 --- a/instantout/fsm.md +++ b/instantout/fsm.md @@ -2,35 +2,41 @@ stateDiagram-v2 [*] --> Init: OnStart BuildHtlc +BuildHtlc --> InstantOutFailed: OnError BuildHtlc --> PushPreimage: OnHtlcSigReceived -BuildHtlc --> InstantFailedOutFailed: OnError -BuildHtlc --> InstantFailedOutFailed: OnRecover +BuildHtlc --> InstantOutFailed: OnRecover FailedHtlcSweep +FailedHtlcSweep --> PublishHtlcSweep: OnRecover +FinishedHtlcPreimageSweep FinishedSweeplessSweep Init +Init --> InstantOutFailed: OnError Init --> SendPaymentAndPollAccepted: OnInit -Init --> InstantFailedOutFailed: OnError -Init --> InstantFailedOutFailed: OnRecover -InstantFailedOutFailed +Init --> InstantOutFailed: OnRecover +InstantOutFailed PublishHtlc PublishHtlc --> FailedHtlcSweep: OnError +PublishHtlc --> PublishHtlcSweep: OnHtlcPublished PublishHtlc --> PublishHtlc: OnRecover -PublishHtlc --> WaitForHtlcSweepConfirmed: OnHtlcBroadcasted +PublishHtlcSweep +PublishHtlcSweep --> FailedHtlcSweep: OnError +PublishHtlcSweep --> WaitForHtlcSweepConfirmed: OnHtlcSweepPublished +PublishHtlcSweep --> PublishHtlcSweep: OnRecover PushPreimage +PushPreimage --> InstantOutFailed: OnError +PushPreimage --> PublishHtlc: OnErrorPublishHtlc PushPreimage --> PushPreimage: OnRecover PushPreimage --> WaitForSweeplessSweepConfirmed: OnSweeplessSweepPublished -PushPreimage --> InstantFailedOutFailed: OnError -PushPreimage --> PublishHtlc: OnErrorPublishHtlc SendPaymentAndPollAccepted +SendPaymentAndPollAccepted --> InstantOutFailed: OnError SendPaymentAndPollAccepted --> BuildHtlc: OnPaymentAccepted -SendPaymentAndPollAccepted --> InstantFailedOutFailed: OnError -SendPaymentAndPollAccepted --> InstantFailedOutFailed: OnRecover +SendPaymentAndPollAccepted --> InstantOutFailed: OnRecover WaitForHtlcSweepConfirmed +WaitForHtlcSweepConfirmed --> FailedHtlcSweep: OnError WaitForHtlcSweepConfirmed --> FinishedHtlcPreimageSweep: OnHtlcSwept WaitForHtlcSweepConfirmed --> WaitForHtlcSweepConfirmed: OnRecover -WaitForHtlcSweepConfirmed --> FailedHtlcSweep: OnError WaitForSweeplessSweepConfirmed -WaitForSweeplessSweepConfirmed --> FinishedSweeplessSweep: OnSweeplessSweepConfirmed -WaitForSweeplessSweepConfirmed --> WaitForSweeplessSweepConfirmed: OnRecover WaitForSweeplessSweepConfirmed --> PublishHtlc: OnError +WaitForSweeplessSweepConfirmed --> WaitForSweeplessSweepConfirmed: OnRecover +WaitForSweeplessSweepConfirmed --> FinishedSweeplessSweep: OnSweeplessSweepConfirmed ``` \ No newline at end of file diff --git a/instantout/reservation/reservation_fsm.md b/instantout/reservation/fsm.md similarity index 57% rename from instantout/reservation/reservation_fsm.md rename to instantout/reservation/fsm.md index 1eefb5c6c..6a48d367a 100644 --- a/instantout/reservation/reservation_fsm.md +++ b/instantout/reservation/fsm.md @@ -2,20 +2,28 @@ stateDiagram-v2 [*] --> Init: OnServerRequest Confirmed -Confirmed --> SpendBroadcasted: OnSpendBroadcasted -Confirmed --> TimedOut: OnTimedOut +Confirmed --> Confirmed: OnError +Confirmed --> Locked: OnLocked Confirmed --> Confirmed: OnRecover +Confirmed --> Spent: OnSpent +Confirmed --> TimedOut: OnTimedOut Failed Init Init --> WaitForConfirmation: OnBroadcast -Init --> Failed: OnRecover Init --> Failed: OnError -SpendBroadcasted -SpendBroadcasted --> SpendConfirmed: OnSpendConfirmed -SpendConfirmed +Init --> Failed: OnRecover +Locked +Locked --> Locked: OnError +Locked --> Locked: OnRecover +Locked --> Spent: OnSpent +Locked --> TimedOut: OnTimedOut +Locked --> Confirmed: OnUnlocked +Spent +Spent --> Spent: OnSpent TimedOut +TimedOut --> TimedOut: OnTimedOut WaitForConfirmation -WaitForConfirmation --> WaitForConfirmation: OnRecover WaitForConfirmation --> Confirmed: OnConfirmed +WaitForConfirmation --> WaitForConfirmation: OnRecover WaitForConfirmation --> TimedOut: OnTimedOut ``` \ No newline at end of file diff --git a/scripts/fsm-generate.sh b/scripts/fsm-generate.sh index 4ab2d74cb..5d2ae1c15 100755 --- a/scripts/fsm-generate.sh +++ b/scripts/fsm-generate.sh @@ -1,4 +1,8 @@ #!/usr/bin/env bash +set -euo pipefail + go run ./fsm/stateparser/stateparser.go --out ./fsm/example_fsm.md --fsm example -go run ./fsm/stateparser/stateparser.go --out ./reservation/reservation_fsm.md --fsm reservation -go run ./fsm/stateparser/stateparser.go --out ./instantout/fsm.md --fsm instantout \ No newline at end of file +go run ./fsm/stateparser/stateparser.go --out ./instantout/reservation/fsm.md --fsm reservation +go run ./fsm/stateparser/stateparser.go --out ./instantout/fsm.md --fsm instantout +go run ./fsm/stateparser/stateparser.go --out ./staticaddr/deposit/fsm.md --fsm staticaddr-deposit +go run ./fsm/stateparser/stateparser.go --out ./staticaddr/loopin/fsm.md --fsm staticaddr-loopin diff --git a/staticaddr/deposit/fsm.md b/staticaddr/deposit/fsm.md new file mode 100644 index 000000000..9a804c6c9 --- /dev/null +++ b/staticaddr/deposit/fsm.md @@ -0,0 +1,52 @@ +```mermaid +stateDiagram-v2 +[*] --> Deposited: OnStart +ChannelPublished +ChannelPublished --> ChannelPublished: OnExpiry +Deposited +Deposited --> Deposited: OnError +Deposited --> PublishExpirySweep: OnExpiry +Deposited --> LoopingIn: OnLoopInInitiated +Deposited --> OpeningChannel: OnOpeningChannel +Deposited --> Deposited: OnRecover +Deposited --> SweepHtlcTimeout: OnSweepingHtlcTimeout +Deposited --> Withdrawing: OnWithdrawInitiated +Expired +Expired --> Expired: OnExpiry +HtlcTimeoutSwept +HtlcTimeoutSwept --> HtlcTimeoutSwept: OnExpiry +LoopedIn +LoopedIn --> LoopedIn: OnExpiry +LoopingIn +LoopingIn --> Deposited: OnError +LoopingIn --> PublishExpirySweep: OnExpiry +LoopingIn --> LoopingIn: OnLoopInInitiated +LoopingIn --> LoopedIn: OnLoopedIn +LoopingIn --> LoopingIn: OnRecover +LoopingIn --> SweepHtlcTimeout: OnSweepingHtlcTimeout +OpeningChannel +OpeningChannel --> ChannelPublished: OnChannelPublished +OpeningChannel --> Deposited: OnError +OpeningChannel --> OpeningChannel: OnExpiry +OpeningChannel --> OpeningChannel: OnRecover +PublishExpirySweep +PublishExpirySweep --> Deposited: OnError +PublishExpirySweep --> WaitForExpirySweep: OnExpiryPublished +PublishExpirySweep --> PublishExpirySweep: OnRecover +SweepHtlcTimeout +SweepHtlcTimeout --> HtlcTimeoutSwept: OnHtlcTimeoutSwept +SweepHtlcTimeout --> SweepHtlcTimeout: OnRecover +WaitForExpirySweep +WaitForExpirySweep --> Deposited: OnError +WaitForExpirySweep --> Expired: OnExpirySwept +WaitForExpirySweep --> PublishExpirySweep: OnRecover +Withdrawing +Withdrawing --> Deposited: OnError +Withdrawing --> Withdrawing: OnExpiry +Withdrawing --> Withdrawing: OnRecover +Withdrawing --> Withdrawing: OnWithdrawInitiated +Withdrawing --> Withdrawn: OnWithdrawn +Withdrawn +Withdrawn --> Withdrawn: OnExpiry +Withdrawn --> Withdrawn: OnWithdrawn +``` \ No newline at end of file diff --git a/staticaddr/loopin/fsm.md b/staticaddr/loopin/fsm.md new file mode 100644 index 000000000..19b22435f --- /dev/null +++ b/staticaddr/loopin/fsm.md @@ -0,0 +1,37 @@ +```mermaid +stateDiagram-v2 +[*] --> InitHtlcTx: OnInitHtlc +Failed +HtlcTimeoutSwept +InitHtlcTx +InitHtlcTx --> UnlockDeposits: OnError +InitHtlcTx --> SignHtlcTx: OnHtlcInitiated +InitHtlcTx --> UnlockDeposits: OnRecover +MonitorHtlcTimeoutSweep +MonitorHtlcTimeoutSweep --> Failed: OnError +MonitorHtlcTimeoutSweep --> HtlcTimeoutSwept: OnHtlcTimeoutSwept +MonitorHtlcTimeoutSweep --> MonitorHtlcTimeoutSweep: OnRecover +MonitorInvoiceAndHtlcTx +MonitorInvoiceAndHtlcTx --> UnlockDeposits: OnError +MonitorInvoiceAndHtlcTx --> PaymentReceived: OnPaymentReceived +MonitorInvoiceAndHtlcTx --> MonitorInvoiceAndHtlcTx: OnRecover +MonitorInvoiceAndHtlcTx --> Failed: OnSwapTimedOut +MonitorInvoiceAndHtlcTx --> SweepHtlcTimeout: OnSweepHtlcTimeout +PaymentReceived +PaymentReceived --> SucceededTransitioningFailed: OnError +PaymentReceived --> Succeeded: OnRecover +PaymentReceived --> Succeeded: OnSucceeded +SignHtlcTx +SignHtlcTx --> UnlockDeposits: OnError +SignHtlcTx --> MonitorInvoiceAndHtlcTx: OnHtlcTxSigned +SignHtlcTx --> UnlockDeposits: OnRecover +Succeeded +SucceededTransitioningFailed +SweepHtlcTimeout +SweepHtlcTimeout --> Failed: OnError +SweepHtlcTimeout --> MonitorHtlcTimeoutSweep: OnHtlcTimeoutSweepPublished +SweepHtlcTimeout --> SweepHtlcTimeout: OnRecover +UnlockDeposits +UnlockDeposits --> Failed: OnError +UnlockDeposits --> UnlockDeposits: OnRecover +``` \ No newline at end of file