onCommandProcessed method

  1. @override
Future<void> onCommandProcessed(
  1. Command command,
  2. List<Event> events
)

Send response messages after successful command processing Only active when aggregate is used as an actor in the actor system

Implementation

@override
Future<void> onCommandProcessed(Command command, List<Event> events) async {
  await super.onCommandProcessed(command, events);

  // A reply prepared by the command handler (signed or funding
  // transaction) is released only now that its events are journaled.
  final awaitingPersist = _repliesAwaitingPersist.remove(command.commandId);

  // Send actor system responses. Key material is already in secure storage:
  // _handleCreateWallet writes it before the event is persisted (audit H4).
  if (_isInActorSystem()) {
    final sender = _capturedSenders[command.commandId];
    if (sender != null) {
      if (awaitingPersist != null) {
        sender.tell(awaitingPersist);
      }
      // A reclaim journals a whole recording (its transaction, its wallet
      // outputs and its hold) and answers once, with the reclaim
      // (libspiffy-87a); the per-event replies below would answer the ask
      // with the first of those instead.
      for (final event in command is ReclaimDeferredSpendCommand
          ? events.whereType<DeferredSpendReclaimedEvent>()
          : events) {
        if (event is WalletCreatedEvent) {
          sender.tell(WalletCreatedResponse(
            walletId: event.walletId,
            rootAddress: event.rootAddress,
            success: true,
          ));
        } else if (event is AddressGeneratedEvent) {
          sender.tell(AddressGeneratedResponse(
            walletId: event.walletId,
            address: event.address,
            derivationIndex: event.derivationIndex,
            chain: event.chain,
            success: true,
            publicKeyHex: event.publicKeyHex,
            metadata: event.metadata,
          ));
        } else if (event is UTXOReceivedEvent) {
          sender.tell(UTXOReceivedResponse(
            walletId: event.walletId,
            txid: event.txid,
            vout: event.vout,
            success: true,
          ));
        } else if (event is TransactionImportedEvent) {
          sender.tell(TransactionRecordedResponse(
            walletId: event.walletId,
            txid: event.txid,
            success: true,
          ));
        } else if (event is UTXOReservedEvent) {
          sender.tell(UTXOReservedResponse(
            walletId: event.walletId,
            utxoKey: '${event.txid}:${event.vout}',
            reservedByTxId: event.reservedByTxId,
            success: true,
          ));
        } else if (event is DeferredTransactionCancelledEvent) {
          sender.tell(DeferredSpendCancelledResponse(
            walletId: event.walletId,
            txid: event.txid,
            success: true,
            releasedUtxoKeys: [for (final r in event.releasedInputs) r.utxoKey],
          ));
        } else if (event is DeferredSpendReclaimedEvent) {
          sender.tell(DeferredSpendReclaimedResponse(
            walletId: event.walletId,
            txid: event.txid,
            reclaimTxid: event.reclaimTxid,
            success: true,
            reclaimedUtxoKeys: event.reclaimedUtxoKeys,
          ));
        }
      }
      // The recording is journaled (or was already, bead libspiffy-viy):
      // a caller that must not act before it (BenfordCoordinatorActor
      // broadcasts only after this, bead libspiffy-ypp) waits for this.
      if (command is RecordOutgoingTransactionCommand) {
        // The amount comes off the event this command journaled, not off
        // the command: it is what the wallet recorded, and it is absent
        // when nothing was journaled because the transaction was recorded
        // already (bead libspiffy-viy). An absence, never a zero (bead
        // libspiffy-5ml6).
        final recorded = events.whereType<TransactionRecordedEvent>().firstOrNull;
        sender.tell(TransactionRecordedResponse(
          walletId: command.walletId,
          txid: command.txid,
          paymentAmount: recorded == null ? null : BigInt.tryParse(recorded.paymentAmount),
          success: true,
        ));
      }
      // Answered also when nothing was journaled (already watched or owned).
      if (command is AddWatchAddressCommand) {
        sender.tell(WatchAddressAddedResponse(
          walletId: command.walletId,
          address: command.address,
          success: true,
          journaled: events.isNotEmpty,
        ));
      }
    }
  }

  // The WalletCreatedEvent is journaled, so the key material written in
  // _handleCreateWallet is now committed: stop tracking it for rollback.
  if (command is CreateWalletCommand) {
    _keyMaterialAwaitingPersist.remove(command.commandId);
  }
}