83: Remove lifecycle operations from the EvelynIOUBurnerService API
This commit is contained in:
@@ -186,6 +186,10 @@ nenjim.journal.service</code></pre>
|
||||
<code>NenjimProcess</code> so the Hub owns startup. The implementation starts its configured active
|
||||
<code>PriceSource</code> instances internally and stops those already started if Ticker startup fails; it
|
||||
exposes no general Ticker stop operation.</p>
|
||||
<p><code>EvelynIOUBurnerService</code> follows the same separation even though its current domain-service view is
|
||||
empty. It supplies typed Registry and dependency identity without external controls, while the same
|
||||
<code>EvelynIOUBurnerServiceImpl</code> object separately implements <code>NenjimProcess</code> for Hub-owned
|
||||
startup. Neither the service interface nor the implementation exposes an ordinary stop or restart operation.</p>
|
||||
<p>The Ticker still explicitly selects the Raydium EVE/USDT source ID while the hardcoded source remains a passive,
|
||||
registered component. Process Manager continues to poll immutable ordered snapshots of actual successful
|
||||
startup milestones rather than displaying configured intent or claiming current health.</p>
|
||||
|
||||
@@ -204,6 +204,11 @@ samme `TickerServiceImpl`-objekt separat er en `NenjimProcess`, som Hub'en start
|
||||
implementation-owned; `PriceSource`-instanser startes internt, og en fejlet Ticker-start stopper de sources, som nåede
|
||||
at starte. Der findes ingen almindelig Ticker-serviceoperation til start eller stop.
|
||||
|
||||
Evelyn IOU Burner følger samme opdeling, selv om dens nuværende domain-service-view er tomt:
|
||||
`EvelynIOUBurnerService` giver typed Registry- og dependency-identitet uden eksterne håndtag, mens det samme
|
||||
`EvelynIOUBurnerServiceImpl`-objekt separat er en Hub-styret `NenjimProcess`. Kun process-view'et eksponerer startup;
|
||||
hverken service-interfacet eller implementationen eksponerer almindelig stop eller restart.
|
||||
|
||||
En `NenjimComponentId` er registration metadata i ét Registry og er ikke en egenskab ved objektet. ID-formatet er
|
||||
lower-case ASCII med dot-separated segments og valgfrie interne bindestreger, f.eks. `nenjim.registry.service` og
|
||||
`assetaz.price-source.raydium-pool.eve-usdt`. Det samme objekt må registreres under flere forskellige ID'er, og én
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
| JavaFX runtime | Det ene JVM-wide JavaFX toolkit, som udfører UI-arbejde på JavaFX Application Thread. Når processen er valgt, ejer Hub'en dets eneste startup-sti gennem `JavaFxRuntimeServiceImpl` og holder det aktivt efter sidste lukkede vindue med `Platform.setImplicitExit(false)`. En headless konfiguration kan udelade runtime og alle JavaFX-applikationer helt. |
|
||||
| JavaFxRuntimeService | Registrerbart service-view med kun non-null `runLater(Runnable)`. Samme referenceimplementation er en Hub-styret `NenjimProcess`; service-consumers får den gennem constructor injection, men får ingen lifecycle-, status-, configuration-, window-, event-, stop- eller restart-operationer. |
|
||||
| TickerService | Lifecycle-frit domain/query-view med kun latest-price lookup. Samme `TickerServiceImpl`-objekt er separat en Hub-styret `NenjimProcess`; det ejer `PriceSource`-startup internt og stopper allerede startede sources som rollback, hvis Ticker-start fejler, men eksponerer ingen almindelig start- eller stop-operation. |
|
||||
| EvelynIOUBurnerService | Tomt, lifecycle-frit domain-service-view, som bevarer typed Registry- og dependency-identitet, indtil eksterne domain-operationer findes. Samme `EvelynIOUBurnerServiceImpl`-objekt er separat en Hub-styret `NenjimProcess`; service-consumers får ingen start-, stop- eller restart-operationer. |
|
||||
| Context | Fremtidig beskrivelse af ønskede/resolverede artifactvalg og isoleret runtime; ikke implementeret af Journal-laget eller første Registry-version. |
|
||||
| Resolver | Fremtidig komponent for dependency graph og release-selection; Journal-laget repræsenterer reglerne, men vælger ikke en graf. |
|
||||
|
||||
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-09-04
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
## Context
|
||||
|
||||
The Burner implementation is already both `EvelynIOUBurnerService` and `NenjimProcess`, but the service contract contains no domain operation: it exposes only duplicated startup and an implementation-specific stop lifecycle. The canonical `NenjimProcess` contract supports only Hub-owned `start()` and deliberately provides no common stop or restart.
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
|
||||
- Preserve `EvelynIOUBurnerService` as an empty typed component contract for Registry lookup and dependency injection.
|
||||
- Keep startup exclusively on the implementation's `NenjimProcess` view.
|
||||
- Remove public normal stop and the internal fields/helpers used only by that operation.
|
||||
- Preserve immediate fixed-delay worker startup and cleanup if worker scheduling fails.
|
||||
- Describe loss of process-local uncertainty state only across a complete process restart.
|
||||
|
||||
**Non-Goals:**
|
||||
|
||||
- No stop or restart addition to `NenjimProcess`.
|
||||
- No Hub shutdown or worker-control API.
|
||||
- No new external Burner domain operation.
|
||||
- No transaction, polling, notification, dependency, or test behavior change.
|
||||
|
||||
## Decisions
|
||||
|
||||
### Keep an empty typed service view
|
||||
|
||||
The empty interface remains a `NenjimComponent`. This preserves the domain-specific type, Registry visibility, and future extension point without leaking Hub-owned lifecycle to consumers or exposing the concrete implementation.
|
||||
|
||||
### Retain only process startup
|
||||
|
||||
`EvelynIOUBurnerServiceImpl.start()` remains unchanged in responsibility and implements `NenjimProcess.start()`. The Hub already constructs and registers the same object, resolves it through `NenjimProcess`, and starts it through that view.
|
||||
|
||||
### Remove general stop state without weakening failed-start cleanup
|
||||
|
||||
Delete public `stop()`, its `stopping` flag, termination wait timeout, and interruption-aware stop helper. The `start()` scheduling failure path continues to call `shutdownNow()`, clear the worker reference, reset process-local state, and throw before startup succeeds. A successful process has no public stop/restart path until Nenjim defines one centrally.
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- [A successful worker cannot be stopped through this implementation] → This is intentional; lifecycle expansion belongs in the common Hub/process architecture.
|
||||
- [An empty service interface has no callable API] → It still supplies meaningful typed identity and prevents consumers from depending on implementation classes.
|
||||
- [Operational recovery now requires process restart] → Document that restart forgets pending uncertainty state and requires external reconciliation first.
|
||||
|
||||
## Migration Plan
|
||||
|
||||
1. Recompile consumers against the empty service interface.
|
||||
2. Remove direct Burner start/stop calls and select startup through the existing Hub process configuration.
|
||||
3. Restart the complete process only after reconciling uncertain on-chain activity; no data or configuration migration is required.
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
## Why
|
||||
|
||||
`EvelynIOUBurnerService` currently contains only `start()` and `stop()`, so ordinary service consumers receive process control instead of a domain API. This conflicts with the current Nenjim architecture, where the Hub owns lifecycle through an implementation's separate `NenjimProcess` view.
|
||||
|
||||
## What Changes
|
||||
|
||||
- **BREAKING**: Make `EvelynIOUBurnerService` an empty lifecycle-free typed service contract until external domain operations exist.
|
||||
- Keep `EvelynIOUBurnerServiceImpl.start()` solely as its `NenjimProcess.start()` implementation.
|
||||
- Remove the implementation's public general stop operation and its now-unused stopping state and termination timeout.
|
||||
- Preserve startup-failure cleanup, worker scheduling, transaction safety, and Hub startup wiring.
|
||||
- Replace explicit stop/start reset semantics with process-restart semantics in JavaDoc, OpenSpec, and Nenjim documentation.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
- `evelyn-iou-burner-service`: Separates the empty typed service view from Hub-owned process startup and removes unsupported public stop/restart control.
|
||||
|
||||
## Impact
|
||||
|
||||
- Changes the public `com.fanitas.evelyn.service.burner.EvelynIOUBurnerService` source contract and public methods of its reference implementation.
|
||||
- Existing direct lifecycle callers must use Hub process configuration for startup and cannot request shutdown until common process stop semantics exist.
|
||||
- Adds no Hub shutdown, `NenjimProcess.stop()`, replacement domain operation, dependency, or automated test.
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Evelyn-specific public service boundary
|
||||
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 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** 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: Immediate sequential fixed-delay lifecycle
|
||||
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 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 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** `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 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: 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-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
|
||||
- **THEN** no second input is attempted in that iteration, the EVE balance is still reloaded, and a later iteration may attempt a new swap
|
||||
|
||||
#### 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 external reconciliation and a complete process restart
|
||||
|
||||
#### Scenario: Swap returns normally
|
||||
- **WHEN** Jupiter returns a successful result whose transaction already reached `CONFIRMED`
|
||||
- **THEN** the burner performs no additional confirmation wait and reloads the wallet instead of deriving its burn amount only from the returned output amount
|
||||
|
||||
### 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 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
|
||||
- **THEN** pending state is retained before the service begins finalization waiting
|
||||
|
||||
#### 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 external reconciliation and a complete process restart
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
## 1. Burner API and implementation
|
||||
|
||||
- [x] 1.1 Remove `start()` and `stop()` from `EvelynIOUBurnerService` while retaining its typed `NenjimComponent` identity.
|
||||
- [x] 1.2 Keep implementation startup through `NenjimProcess` and remove public stop plus stop-only state and helpers.
|
||||
- [x] 1.3 Preserve worker-scheduling failure cleanup and unchanged Hub startup wiring.
|
||||
|
||||
## 2. Specification and documentation
|
||||
|
||||
- [x] 2.1 Update the Burner capability for a lifecycle-free service view, Hub-owned startup, and process-restart-only reset semantics.
|
||||
- [x] 2.2 Update relevant JavaDoc and Nenjim architecture, public, and terminology documentation.
|
||||
|
||||
## 3. Verification
|
||||
|
||||
- [x] 3.1 Run strict OpenSpec validation and archive the completed change into the canonical specification.
|
||||
- [x] 3.2 Run the complete Gradle build and inspect the compiled public API and final diff.
|
||||
@@ -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.
|
||||
|
||||
@@ -58,34 +58,15 @@ import java.util.List;
|
||||
* repeats the completed burn.</li>
|
||||
* </ol>
|
||||
*
|
||||
* <p>One dedicated worker begins immediately on {@link #start()}, uses fixed
|
||||
* <p>One dedicated worker begins immediately when the Hub starts the reference
|
||||
* implementation through its separate {@code NenjimProcess} view, uses fixed
|
||||
* delay measured after each completed iteration, and never overlaps
|
||||
* iterations. {@link #stop()} prevents new iterations and stops that worker.
|
||||
* Pending signatures and swap/burn suspension flags are process-local only
|
||||
* and are deliberately reset by a completed explicit stop/start cycle or a
|
||||
* process restart. Such a reset forgets unknown transaction state and can
|
||||
* weaken duplicate prevention; operators should reconcile uncertain on-chain
|
||||
* activity before deliberately restarting a suspended or pending service.</p>
|
||||
* iterations. This service view currently exposes no domain operations and no
|
||||
* lifecycle control. Pending signatures and swap/burn suspension flags are
|
||||
* process-local only and are reset by a process restart. Such a reset forgets
|
||||
* unknown transaction state and can weaken duplicate prevention; operators
|
||||
* should reconcile uncertain on-chain activity before deliberately restarting
|
||||
* a suspended or pending process.</p>
|
||||
*/
|
||||
public interface EvelynIOUBurnerService extends NenjimComponent {
|
||||
/**
|
||||
* Starts the dedicated worker and begins the first iteration immediately.
|
||||
*
|
||||
* @throws IllegalStateException if this instance is already running or a
|
||||
* prior worker has not terminated cleanly
|
||||
*/
|
||||
void start();
|
||||
|
||||
/**
|
||||
* Prevents new iterations, interrupts current processing, and stops the
|
||||
* dedicated worker cleanly.
|
||||
*
|
||||
* <p>A completed stop clears process-local pending and suspension state.
|
||||
* If the calling thread is interrupted while waiting for termination, its
|
||||
* interrupt status is preserved.</p>
|
||||
*
|
||||
* @throws IllegalStateException if clean worker termination cannot be
|
||||
* established
|
||||
*/
|
||||
void stop();
|
||||
}
|
||||
|
||||
+5
-67
@@ -46,7 +46,9 @@ import static com.r35157.assetaz.services.cis.CurrencyTypeIds.USDT_ID;
|
||||
import static com.r35157.assetaz.services.cis.ExternalCurrencyReference.SOLANA_MINT_NAMESPACE;
|
||||
|
||||
/**
|
||||
* Reference implementation of the Evelyn IOU buyback-and-burn lifecycle.
|
||||
* Reference Evelyn IOU burner whose domain service view is exposed through
|
||||
* {@link EvelynIOUBurnerService} and whose startup is owned by the Hub through
|
||||
* {@link NenjimProcess}.
|
||||
*/
|
||||
public final class EvelynIOUBurnerServiceImpl
|
||||
implements EvelynIOUBurnerService, NenjimProcess {
|
||||
@@ -202,9 +204,9 @@ public final class EvelynIOUBurnerServiceImpl
|
||||
|
||||
@Override
|
||||
public synchronized void start() {
|
||||
if (worker != null || stopping) {
|
||||
if (worker != null) {
|
||||
throw new IllegalStateException(
|
||||
"Evelyn IOU burner is already running or stopping"
|
||||
"Evelyn IOU burner is already running"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -247,70 +249,12 @@ public final class EvelynIOUBurnerServiceImpl
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
ScheduledExecutorService workerToStop;
|
||||
synchronized (this) {
|
||||
if (worker == null) {
|
||||
resetProcessLocalState();
|
||||
return;
|
||||
}
|
||||
if (stopping) {
|
||||
throw new IllegalStateException(
|
||||
"Evelyn IOU burner is already stopping"
|
||||
);
|
||||
}
|
||||
stopping = true;
|
||||
workerToStop = worker;
|
||||
}
|
||||
|
||||
workerToStop.shutdownNow();
|
||||
boolean terminated;
|
||||
try {
|
||||
terminated = workerToStop.awaitTermination(
|
||||
WORKER_TERMINATION_TIMEOUT_SECONDS,
|
||||
TimeUnit.SECONDS
|
||||
);
|
||||
} catch (InterruptedException exception) {
|
||||
workerToStop.shutdownNow();
|
||||
boolean terminatedAfterInterruption = workerToStop.isTerminated();
|
||||
synchronized (this) {
|
||||
if (terminatedAfterInterruption && worker == workerToStop) {
|
||||
worker = null;
|
||||
resetProcessLocalState();
|
||||
}
|
||||
stopping = false;
|
||||
}
|
||||
Thread.currentThread().interrupt();
|
||||
throw new IllegalStateException(
|
||||
"Interrupted while stopping Evelyn IOU burner",
|
||||
exception
|
||||
);
|
||||
}
|
||||
|
||||
synchronized (this) {
|
||||
if (!terminated) {
|
||||
stopping = false;
|
||||
throw new IllegalStateException(
|
||||
"Evelyn IOU burner worker did not terminate cleanly"
|
||||
);
|
||||
}
|
||||
if (worker == workerToStop) {
|
||||
worker = null;
|
||||
}
|
||||
resetProcessLocalState();
|
||||
stopping = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void runIterationSafely() {
|
||||
try {
|
||||
runIteration();
|
||||
} catch (InterruptedException exception) {
|
||||
Thread.currentThread().interrupt();
|
||||
if (!isStopping()) {
|
||||
log.warn("Evelyn IOU burner iteration was interrupted");
|
||||
}
|
||||
} catch (RuntimeException exception) {
|
||||
log.error(
|
||||
"Unexpected Evelyn IOU burner iteration failure",
|
||||
@@ -991,10 +935,6 @@ public final class EvelynIOUBurnerServiceImpl
|
||||
return duration;
|
||||
}
|
||||
|
||||
private synchronized boolean isStopping() {
|
||||
return stopping;
|
||||
}
|
||||
|
||||
private void resetProcessLocalState() {
|
||||
pendingBurn = null;
|
||||
swapsSuspended = false;
|
||||
@@ -1011,7 +951,6 @@ public final class EvelynIOUBurnerServiceImpl
|
||||
private static final int MAXIMUM_SLIPPAGE_BPS = 10000;
|
||||
private static final ΩamountDecimalsΩ NATIVE_SOLANA_DECIMALS = 9;
|
||||
private static final ΩamountDecimalsΩ MAXIMUM_SPL_TOKEN_DECIMALS = 255;
|
||||
private static final long WORKER_TERMINATION_TIMEOUT_SECONDS = 10;
|
||||
private static final String WORKER_THREAD_NAME =
|
||||
"evelyn-iou-buyback-and-burner";
|
||||
|
||||
@@ -1032,7 +971,6 @@ public final class EvelynIOUBurnerServiceImpl
|
||||
private final TradingPair eveUsdtTradingPair;
|
||||
|
||||
private ScheduledExecutorService worker;
|
||||
private boolean stopping;
|
||||
private boolean swapsSuspended;
|
||||
private boolean burnsSuspended;
|
||||
private PendingBurn pendingBurn;
|
||||
|
||||
Reference in New Issue
Block a user