67: Add generic awaitTransaction() support to SolanaBlockChain

This commit is contained in:
2026-08-11 11:42:41 +02:00
parent e7f551b8dd
commit cb2d3e2a16
11 changed files with 838 additions and 14 deletions
@@ -0,0 +1,98 @@
# solana-transaction-awaiting Specification
## Purpose
Defines how callers await a definitive Solana transaction outcome at an explicitly requested commitment without resubmitting the transaction.
## Requirements
### Requirement: Separate transaction awaiting API
The Solana blockchain API SHALL expose a blocking operation that accepts an existing 64-byte Base58 transaction signature, a requested commitment of `PROCESSED`, `CONFIRMED`, or `FINALIZED`, and a strictly positive overall timeout. Submission and awaiting SHALL remain separate operations, and existing consumers SHALL NOT begin awaiting automatically.
#### Scenario: Valid await request
- **WHEN** a caller supplies a valid signature, non-null commitment, and positive timeout
- **THEN** the service observes that already submitted transaction without submitting or resubmitting it
#### Scenario: Invalid arguments
- **WHEN** the signature is null, blank, invalid Base58, or does not decode to exactly 64 bytes, the commitment is null, or the timeout is null, zero, or negative
- **THEN** the service rejects the call locally before making an RPC request
### Requirement: Commitment-aware definitive outcomes
The commitment order SHALL be `PROCESSED < CONFIRMED < FINALIZED`, and an observed level SHALL satisfy the same or any lower requested level. The service SHALL return `SUCCEEDED` or `FAILED` only after the observed transaction has reached or exceeded the requested commitment. `FAILED` SHALL exclusively represent a non-null Solana on-chain `err`; lower-commitment success or error observations SHALL remain inconclusive.
#### Scenario: Success reaches requested commitment
- **WHEN** the transaction is observed at or above the requested commitment with `err: null`
- **THEN** the service returns `SUCCEEDED` with the transaction slot and no failure details
#### Scenario: Failure reaches requested commitment
- **WHEN** the transaction is observed at or above the requested commitment with a non-null `err`
- **THEN** the service returns `FAILED` with the transaction slot and the complete `err` value as compact JSON
#### Scenario: Observation is below requested commitment
- **WHEN** a successful or failed transaction is observed below the requested commitment
- **THEN** the service continues polling rather than returning a definitive outcome
### Requirement: Transaction outcome invariants
Every public transaction outcome SHALL enforce that `SUCCEEDED` has a non-null slot and null failure details, `FAILED` has a non-null slot and non-blank failure details, and `TIMED_OUT` has null slot and null failure details.
#### Scenario: Invalid outcome construction
- **WHEN** an outcome is constructed with fields inconsistent with its status
- **THEN** construction fails immediately
### Requirement: Signature status polling contract
The service SHALL poll HTTP JSON-RPC `getSignatureStatuses` with exactly the supplied signature and `searchTransactionHistory: true`. It SHALL use only `result.value[0].slot`, `err`, and `confirmationStatus`; a null sole value SHALL remain inconclusive. A present status SHALL contain a Long-representable slot and one of `processed`, `confirmed`, or `finalized`.
#### Scenario: Signature is not found yet
- **WHEN** `result.value` contains exactly one null element
- **THEN** the service continues polling until a definitive outcome or timeout
#### Scenario: Valid status is observed
- **WHEN** the sole status contains a valid slot, recognized confirmation status, and `err` value
- **THEN** the service evaluates it against the requested commitment
### Requirement: Overall monotonic deadline
The timeout SHALL define one monotonic deadline covering RPC-gate acquisition, shared throttling, HTTP calls, response processing, and subsequent polls. Gate and throttle waiting SHALL be interruptible and deadline-aware, no request SHALL start after the deadline, and an HTTP request SHALL as far as reasonably possible be bounded by the remaining time.
#### Scenario: Deadline expires before RPC access
- **WHEN** the deadline expires while waiting for the shared gate or remaining throttle interval
- **THEN** the service returns `TIMED_OUT` without starting another RPC request
#### Scenario: Deadline expires during HTTP request
- **WHEN** the deadline-bound status HTTP request times out
- **THEN** the service returns `TIMED_OUT`
#### Scenario: Caller is interrupted
- **WHEN** interruption occurs during gate acquisition, throttling, HTTP communication, or another internal wait
- **THEN** `InterruptedException` propagates directly and no subsequent request is made
### Requirement: Unknown timeout semantics
`TIMED_OUT` SHALL mean the requested definitive outcome remains unknown and SHALL NOT imply rejection, failure, expiration, or permission to resubmit. It SHALL not retain a slot previously observed below the requested commitment.
#### Scenario: Deadline passes without definitive outcome
- **WHEN** the signature remains unseen or below the requested commitment until the deadline
- **THEN** the service returns `TIMED_OUT` with null slot and null failure details and does not resubmit the transaction
### Requirement: Shared RPC throttling
Status polling SHALL reuse the same shared mechanism that preserves at least five seconds between Solana RPC calls. It SHALL add no independent polling sleep, interval, or limiter, and existing RPC operations SHALL preserve their public behavior and common throttling.
#### Scenario: Inconclusive poll
- **WHEN** a status response is inconclusive and time remains
- **THEN** the next poll is naturally delayed by the shared five-second RPC throttle only
### Requirement: Communication and protocol errors
Non-success HTTP responses, JSON-RPC errors, invalid JSON, missing or invalid `result.value`, arrays with other than exactly one element, invalid slots, missing or unknown confirmation status, and other network or protocol failures SHALL result in `IOException`, not `FAILED`. Error messages SHALL identify `getSignatureStatuses` and include relevant context. The service SHALL NOT automatically retry after an `IOException`.
#### Scenario: Invalid RPC response
- **WHEN** a status response violates the required HTTP, JSON-RPC, result-array, slot, or confirmation-status contract
- **THEN** the service throws a contextual `IOException`
#### Scenario: Communication failure
- **WHEN** status communication fails for a reason other than expiration of the overall deadline or thread interruption
- **THEN** the service throws `IOException` without automatically polling again
### Requirement: Cached decorator delegates directly
The cached Solana blockchain decorator SHALL delegate every transaction-await call directly to its underlying blockchain without caching, reuse, or deduplication.
#### Scenario: Repeated awaits through cached decorator
- **WHEN** callers invoke transaction awaiting multiple times through the cached decorator
- **THEN** every invocation reaches the delegate independently