-
Notifications
You must be signed in to change notification settings - Fork 9
oor: add OOR_REJECT_USER_BALANCE typed reject code + classifier #807
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,85 @@ | ||||||||||
| package oor | ||||||||||
|
|
||||||||||
| import ( | ||||||||||
| "errors" | ||||||||||
| "fmt" | ||||||||||
| "testing" | ||||||||||
|
|
||||||||||
| "github.com/lightninglabs/darepo-client/rpc/oorpb" | ||||||||||
| "github.com/stretchr/testify/require" | ||||||||||
| ) | ||||||||||
|
|
||||||||||
| // TestClassifySubmitError verifies each typed operator rejection code maps to | ||||||||||
| // its client-facing typed error, that the user-balance rejection is distinct | ||||||||||
| // from the output-policy one (they have opposite retry semantics), and that | ||||||||||
| // untyped errors pass through unchanged. | ||||||||||
| func TestClassifySubmitError(t *testing.T) { | ||||||||||
| t.Parallel() | ||||||||||
|
|
||||||||||
| t.Run("user balance maps to typed error", func(t *testing.T) { | ||||||||||
| t.Parallel() | ||||||||||
|
|
||||||||||
| rejected := &oorpb.SubmitRejectedError{ | ||||||||||
| Code: oorpb.OORRejectCode_OOR_REJECT_USER_BALANCE, | ||||||||||
| Reason: "user balance exceeds maximum", | ||||||||||
| } | ||||||||||
|
|
||||||||||
| got := ClassifySubmitError(rejected) | ||||||||||
| require.ErrorIs(t, got, &ErrUserBalanceExceeded{}) | ||||||||||
| require.Contains(t, got.Error(), "user balance exceeds maximum") | ||||||||||
| }) | ||||||||||
|
|
||||||||||
| t.Run("user balance distinct from output policy", func(t *testing.T) { | ||||||||||
| t.Parallel() | ||||||||||
|
|
||||||||||
| balance := ClassifySubmitError(&oorpb.SubmitRejectedError{ | ||||||||||
| Code: oorpb.OORRejectCode_OOR_REJECT_USER_BALANCE, | ||||||||||
| }) | ||||||||||
| policy := ClassifySubmitError(&oorpb.SubmitRejectedError{ | ||||||||||
| Code: oorpb.OORRejectCode_OOR_REJECT_OUTPUT_POLICY, | ||||||||||
| }) | ||||||||||
|
|
||||||||||
| // A balance rejection must not be mistaken for a (terminal) | ||||||||||
| // output-policy rejection, since only the latter requires | ||||||||||
| // restructuring the outputs. | ||||||||||
| require.NotErrorIs(t, balance, &ErrOutputPolicyViolation{}) | ||||||||||
| require.NotErrorIs(t, policy, &ErrUserBalanceExceeded{}) | ||||||||||
|
Comment on lines
+45
to
+46
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The
Suggested change
|
||||||||||
| }) | ||||||||||
|
|
||||||||||
| t.Run("output policy still maps", func(t *testing.T) { | ||||||||||
| t.Parallel() | ||||||||||
|
|
||||||||||
| got := ClassifySubmitError(&oorpb.SubmitRejectedError{ | ||||||||||
| Code: oorpb.OORRejectCode_OOR_REJECT_OUTPUT_POLICY, | ||||||||||
| Reason: "output 0 exceeds the per-VTXO maximum", | ||||||||||
| }) | ||||||||||
| require.ErrorIs(t, got, &ErrOutputPolicyViolation{}) | ||||||||||
| }) | ||||||||||
|
|
||||||||||
| t.Run("wrapped rejection is unwrapped", func(t *testing.T) { | ||||||||||
| t.Parallel() | ||||||||||
|
|
||||||||||
| // errors.As must reach the typed rejection even when it is | ||||||||||
| // wrapped, so the daemon's outer context does not hide the | ||||||||||
| // code. | ||||||||||
| wrapped := fmt.Errorf("submit failed: %w", | ||||||||||
| &oorpb.SubmitRejectedError{ | ||||||||||
| Code: oorpb. | ||||||||||
| OORRejectCode_OOR_REJECT_USER_BALANCE, | ||||||||||
| }) | ||||||||||
|
|
||||||||||
| require.ErrorIs( | ||||||||||
| t, ClassifySubmitError(wrapped), | ||||||||||
| &ErrUserBalanceExceeded{}, | ||||||||||
| ) | ||||||||||
| }) | ||||||||||
|
|
||||||||||
| t.Run("nil and untyped pass through", func(t *testing.T) { | ||||||||||
| t.Parallel() | ||||||||||
|
|
||||||||||
| require.NoError(t, ClassifySubmitError(nil)) | ||||||||||
|
|
||||||||||
| plain := errors.New("connection reset") | ||||||||||
| require.Equal(t, plain, ClassifySubmitError(plain)) | ||||||||||
| }) | ||||||||||
| } | ||||||||||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When arkd returns
OOR_REJECT_USER_BALANCEon the async SubmitPackage response, the only production consumer I found (darepod/server.go'sregisterOOREventRoutes) calls this classifier and immediately flattens the result toclassified.Error()inOutboxErrorEvent.ErrorReason;GetOORSession/ListOORSessionsthen expose only that string. That means swapdk/custodial callers still cannot route on the new transient code witherrors.Asor the enum without string-matching the failure reason, which is the recovery split this code is meant to provide. Please persist/expose the reject code (or a typed failure cause) through the session status/daemon API instead of only constructing a local typed error here.Useful? React with 👍 / 👎.