83: Remove lifecycle operations from the EvelynIOUBurnerService API

This commit is contained in:
2026-09-04 10:04:47 +02:00
parent 681b6feef1
commit b6a6d544b5
11 changed files with 183 additions and 111 deletions
@@ -3,19 +3,17 @@
## Purpose
Defines the Evelyn-specific service that safely converts prioritized wallet assets into Evelyn IOU, burns the complete visible EVE balance, and announces only finalized burns without automatically repeating transactions whose outcomes may be unknown.
## Requirements
### Requirement: Evelyn-specific public service boundary
The system SHALL expose `EvelynIOUBurnerService` under `com.fanitas.evelyn.service.burner` with only `start()` and `stop()` operations, and SHALL keep its reference implementation under `com.fanitas.evelyn.service.burner.impl.ref`. The service SHALL remain specific to Evelyn IOU and SHALL NOT introduce a generic token-burner abstraction, burner-specific input interfaces, records, or class hierarchies.
The system SHALL expose an empty lifecycle-free `EvelynIOUBurnerService` under `com.fanitas.evelyn.service.burner` as the typed domain service and Registry view until external domain operations exist, and SHALL keep its reference implementation under `com.fanitas.evelyn.service.burner.impl.ref`. The same reference implementation object SHALL implement both `EvelynIOUBurnerService` and `NenjimProcess`; the Hub SHALL start it only through its `NenjimProcess` view. Neither the service interface nor the implementation SHALL expose a general stop, restart, worker-control, or other lifecycle operation. The service SHALL remain specific to Evelyn IOU and SHALL NOT introduce a generic token-burner abstraction, burner-specific input interfaces, records, or class hierarchies.
#### Scenario: Downstream lifecycle consumer
- **WHEN** a downstream component depends on the Evelyn burner API
- **THEN** it can start and stop the service without depending on reference-implementation types or a generic burner model
- **THEN** it receives a typed component contract with no callable lifecycle or domain operation and does not depend on reference-implementation types or a generic burner model
#### Scenario: Input model remains domain-level
- **WHEN** callers configure accepted buyback currencies
- **THEN** they use existing `MoneyAmount` values rather than burner-specific Solana, SPL-token, or input-asset types
- **WHEN** the Hub configures accepted buyback currencies while constructing the process implementation
- **THEN** it uses existing `MoneyAmount` values rather than burner-specific Solana, SPL-token, or input-asset types
### Requirement: Ordered AssetAZ input configuration
The authoritative constructor SHALL accept a non-null ordered `List<MoneyAmount>` named `inputCurrencyMinimumReserves`, defensively copy it without changing order, and interpret inclusion as acceptance, list position as priority, each `currencyType().id()` as the authoritative AssetAZ identity, and each `amount()` as the minimum balance that must remain. It SHALL ignore supplied currency name and symbol metadata by resolving each UUID through the injected Currency Identity Service and using the canonical returned currency for pricing and processing. An empty list SHALL be valid and SHALL still permit directly deposited EVE to be burned.
@@ -65,21 +63,21 @@ The service SHALL fix the EVE mint as `meveYG2iXYSkgSUn1T1uxcthH1EGMZdRHGgCntXZA
- **THEN** it uses the fixed EVE mint and the canonical EVE/USDT pair without accepting replacements through construction
### Requirement: Immediate sequential fixed-delay lifecycle
Each service instance SHALL own at most one dedicated background worker. A successful `start()` SHALL begin the first iteration immediately; every later iteration SHALL begin only after the previous iteration completes and the configured fixed delay elapses; and iterations SHALL never overlap. `stop()` SHALL prevent new iterations, interrupt and stop the worker cleanly, and preserve interruption if the stopping thread is interrupted. A repeated start while active SHALL NOT create another worker.
Each reference implementation instance SHALL own at most one dedicated background worker. A successful Hub-owned `NenjimProcess.start()` SHALL begin the first iteration immediately; every later iteration SHALL begin only after the previous iteration completes and the configured fixed delay elapses; and iterations SHALL never overlap. A repeated process start while active SHALL NOT create another worker. If scheduling the worker fails during startup, the implementation SHALL stop the newly created executor, clear its worker reference and process-local state, and report startup failure.
Pending burn information and swap/burn suspension flags SHALL be process-local only. A completed explicit stop followed by start SHALL clear those states. The public JavaDoc SHALL disclose that this reset, or a process restart, loses reconciliation state and can weaken duplicate-prevention until unknown transactions have been reconciled externally.
Pending burn information and swap/burn suspension flags SHALL be process-local only. A complete process restart SHALL clear those states. The public JavaDoc SHALL disclose that restart loses reconciliation state and can weaken duplicate prevention until unknown transactions have been reconciled externally. Neither the service interface nor the implementation SHALL expose ordinary stop or restart operations.
#### Scenario: First and later iterations
- **WHEN** the service starts and one iteration takes longer than usual
- **WHEN** the Hub starts the Burner process and one iteration takes longer than usual
- **THEN** the first iteration starts without initial delay and the next starts only after completion plus the configured interval
#### Scenario: Active service is started again
- **WHEN** `start()` is called while the instance already owns an active worker
- **WHEN** `NenjimProcess.start()` is called while the instance already owns an active worker
- **THEN** the call is rejected without creating an overlapping worker
#### Scenario: Explicit lifecycle reset
- **WHEN** a service with pending or suspended process-local state is cleanly stopped and then explicitly started
- **THEN** the new lifecycle begins without that state and reloads wallet and chain state normally
- **WHEN** a complete process restart occurs after pending or suspended process-local state has been externally reconciled
- **THEN** the new process begins without that prior in-memory state and reloads wallet and chain state normally
### Requirement: Pending burn is handled before all new work
At the start of an iteration, if a prior burn signature is pending, the service SHALL only await that exact signature at `FINALIZED`. It SHALL submit no swap or burn in that iteration. An unknown timeout or I/O outcome SHALL retain the pending state and end the iteration; definitive success SHALL make the one associated notification attempt and end the iteration; and definitive on-chain failure SHALL log the slot and complete failure details, clear pending state, send no notification, and defer any new wallet decision to a later iteration.
@@ -144,7 +142,7 @@ The SOL reserve comparison SHALL use the native balance without estimating or su
### Requirement: At most one confirmed swap attempt per iteration
After selecting a positive exact amount, the service SHALL invoke the existing Jupiter exact-input operation once with the selected CIS-resolved input mint, fixed EVE output mint, selected amount, and configured slippage. Normal return SHALL be treated as already `CONFIRMED` and SHALL NOT trigger a second confirmation wait. Once invoked, no later input currency SHALL be attempted in that iteration regardless of success or failure, and the service SHALL proceed to reload the complete EVE balance whenever processing was not interrupted.
A structured definitive on-chain swap failure SHALL be logged without suspending later-lifecycle swaps. A timed-out structured outcome SHALL suspend all later swaps in that lifecycle. Because the Jupiter `IOException` and interruption contracts can represent an unknown post-submission result, either SHALL conservatively suspend later swaps. The service SHALL never automatically rebuild, re-sign, retry, or resubmit such a swap; while swaps are suspended, later iterations SHALL continue only their burn path.
A structured definitive on-chain swap failure SHALL be logged without suspending later-process swaps. A timed-out structured outcome SHALL suspend all later swaps in that process. Because the Jupiter `IOException` and interruption contracts can represent an unknown post-submission result, either SHALL conservatively suspend later swaps. The service SHALL never automatically rebuild, re-sign, retry, or resubmit such a swap; while swaps are suspended, later iterations SHALL continue only their burn path. Recovery SHALL require external reconciliation followed by a complete process restart, not a Burner service stop/start operation.
#### Scenario: First swap fails definitively
- **WHEN** the first attempted input returns a definitive on-chain failure
@@ -152,7 +150,7 @@ A structured definitive on-chain swap failure SHALL be logged without suspending
#### Scenario: Swap outcome is unknown
- **WHEN** the attempted swap times out, throws an I/O failure that permits unknown post-submission status, or is interrupted
- **THEN** no equivalent or alternative swap is attempted and all later swaps remain suspended until explicit stop/start
- **THEN** no equivalent or alternative swap is attempted and all later swaps remain suspended until external reconciliation and a complete process restart
#### Scenario: Swap returns normally
- **WHEN** Jupiter returns a successful result whose transaction already reached `CONFIRMED`
@@ -183,7 +181,7 @@ Immediately before a new burn, the service SHALL obtain the latest canonical EVE
### Requirement: Single complete-balance burn submission and pending state
For a positive EVE balance with a usable price, the service SHALL call the wallet burn operation exactly once with the fixed EVE mint and complete observed balance. It SHALL NOT automatically retry, rebuild, re-sign, or resubmit within or across iterations. Immediately after a non-blank signature is returned, it SHALL retain process-local pending data containing that signature, submitted EVE amount, and its calculated notification value or complete notification text before awaiting the signature.
If burn submission fails without a usable signature and its outcome may be unknown, including an I/O or interruption path whose contract permits post-submission uncertainty, the service SHALL conservatively suspend further burn submissions for the current lifecycle. It MAY continue ordinary iteration work that does not submit another burn. Explicit stop/start SHALL reset that suspension.
If burn submission fails without a usable signature and its outcome may be unknown, including an I/O or interruption path whose contract permits post-submission uncertainty, the service SHALL conservatively suspend further burn submissions for the current process. It MAY continue ordinary iteration work that does not submit another burn. Recovery SHALL require external reconciliation followed by a complete process restart, not a Burner service stop/start operation.
#### Scenario: Burn returns a signature
- **WHEN** the wallet submits the complete observed EVE balance and returns a non-blank signature
@@ -191,7 +189,7 @@ If burn submission fails without a usable signature and its outcome may be unkno
#### Scenario: Submission outcome may be unknown without a signature
- **WHEN** burn submission fails or is interrupted without providing a usable signature and may already have reached Solana
- **THEN** the service performs no further burn submission until explicit stop/start
- **THEN** the service performs no further burn submission until external reconciliation and a complete process restart
### Requirement: Finalized burn outcome governs completion
Every known burn signature SHALL be awaited through the Solana blockchain at `FINALIZED` with exactly the configured finalization timeout. `SUCCEEDED` SHALL authorize notification; `FAILED` SHALL log the slot and complete on-chain failure details, clear pending state, and send no notification; `TIMED_OUT` and `IOException` SHALL keep the pending signature because its result remains unknown; and `InterruptedException` SHALL preserve interruption and pending state. None of these outcomes SHALL cause automatic transaction resubmission.