handleCommand method

  1. @override
Future<List<Event>> handleCommand(
  1. WalletState currentState,
  2. Command command
)

Handle commands asynchronously and return events

This method supports async cryptographic operations and secure storage access required for wallet operations. All calling code must await this method.

Implementation

@override
Future<List<Event>> handleCommand(WalletState currentState, Command command) async {
  switch (command) {
    case final CreateWalletCommand cmd:
      return await _handleCreateWallet(currentState, cmd);
    case final DeleteWalletCommand cmd:
      return WalletLifecycle.delete(currentState, cmd);
    case final UpdateWalletConfigurationCommand cmd:
      return WalletLifecycle.updateConfiguration(currentState, cmd);
    case final GenerateAddressCommand cmd:
      return await _keys.generateAddress(currentState, cmd);
    case final UpdateAddressLabelCommand cmd:
      return AddressBook.updateAddressLabel(currentState, cmd);
    case final RegisterDiscoveredAddressCommand cmd:
      return AddressBook.registerDiscoveredAddress(currentState, cmd);
    case final AddWatchAddressCommand cmd:
      return AddressBook.addWatchAddress(currentState, cmd);
    case final RecordDelegatedAddressesCommand cmd:
      final recorded = await _keys.recordDelegatedAddresses(currentState, cmd);
      // Sent once the addresses are journaled, and also when every one
      // was recorded already (nothing to journal).
      if (_replyTo(cmd) != null) {
        _repliesAwaitingPersist[cmd.commandId] = DelegatedAddressesRecordedResponse(
          walletId: cmd.walletId,
          addresses: recorded.addresses,
          journaled: [for (final e in recorded.events.whereType<AddressDiscoveredEvent>()) e.address],
          success: true,
        );
      }
      return recorded.events;
    case final RecordType42AddressesCommand cmd:
      final recorded = await _keys.recordType42Addresses(currentState, cmd);
      // Sent once the addresses are journaled, and also when every one
      // was recorded already (nothing to journal).
      if (_replyTo(cmd) != null) {
        _repliesAwaitingPersist[cmd.commandId] = Type42AddressesRecordedResponse(
          walletId: cmd.walletId,
          addresses: recorded.addresses,
          journaled: [for (final e in recorded.events.whereType<Type42AddressRecordedEvent>()) e.address],
          success: true,
        );
      }
      return recorded.events;
    case final DeriveType42DestinationCommand cmd:
      final derived = await _keys.deriveType42Destination(currentState, cmd);
      if (_replyTo(cmd) != null) {
        _repliesAwaitingPersist[cmd.commandId] = Type42DestinationDerivedResponse(
          walletId: cmd.walletId,
          destination: derived.destination,
          success: true,
        );
      }
      return [derived];
    case final IssueAnchorKeyCommand cmd:
      final issued = await _keys.issueAnchorKey(currentState, cmd);
      // Sent once the anchor is journaled, and also when it was issued
      // already (nothing to journal).
      if (_replyTo(cmd) != null) {
        _repliesAwaitingPersist[cmd.commandId] =
            AnchorKeyResponse(walletId: cmd.walletId, publicKeyHex: issued.anchorPublicKey, success: true);
      }
      return issued.events;
    case final SignWithAnchorKeyCommand cmd:
      final anchor = await _keys.anchorKey(
          cmd.walletId, currentState, Type42Derivation.anchorContextHex(cmd.anchorContext));
      final digest = dartsv.sha256(cmd.message);
      final signature = await _keys.cryptoService.signData(anchor, Uint8List.fromList(digest));
      _replyTo(cmd)?.tell(AnchorKeyResponse(
        walletId: cmd.walletId,
        publicKeyHex: anchor.publicKey.toHex(),
        signatureDerHex: hex.encode(signature.toDER()),
        success: true,
      ));
      return const [];
    case final LookupType42AddressesCommand cmd:
      _replyTo(cmd)?.tell(Type42AddressesResponse(
        walletId: cmd.walletId,
        derivations: Type42Book.paidBy(currentState, cmd.rawTransaction),
        success: true,
      ));
      return const [];
    case final ReconcileWatchAddressesCommand cmd:
      return AddressBook.reconcileWatchAddresses(currentState, cmd);
    case final ReceiveUTXOCommand cmd:
      return UtxoLedger.receive(currentState, cmd);
    case final MarkUTXOAvailableCommand cmd:
      return UtxoLedger.markAvailable(currentState, cmd);
    case final RecordImportedTransactionCommand cmd:
      return OutgoingTransactions.recordImported(currentState, cmd);
    case final RecordOutgoingTransactionCommand cmd:
      return _transactions.recordOutgoing(currentState, cmd);
    case final ConfirmTransactionCommand cmd:
      return OutgoingTransactions.confirm(currentState, cmd);
    case final UpdateTransactionStatusCommand cmd:
      return OutgoingTransactions.updateStatus(currentState, cmd);
    case final RevertTransactionConfirmationCommand cmd:
      return OutgoingTransactions.revertConfirmation(currentState, cmd);
    case final SpendUTXOCommand cmd:
      return UtxoLedger.spend(currentState, cmd);
    case final UpdateUTXOConfirmationsCommand cmd:
      return UtxoLedger.updateConfirmations(currentState, cmd);
    case final SignTransactionCommand cmd:
      return await _handleSignTransaction(currentState, cmd);
    case final SignMultisigTransactionCommand cmd:
      return await _handleSignMultisigTransaction(currentState, cmd);
    case final SignInputCommand cmd:
      return await _handleSignInput(currentState, cmd);
    case final BuildFundingTransactionCommand cmd:
      return await _handleBuildFundingTransaction(currentState, cmd);
    case final BroadcastTransactionCommand cmd:
      return OutgoingTransactions.broadcast(currentState, cmd);
    case final ReserveUTXOsCommand cmd:
      return _reservations.reserveMany(currentState, cmd);
    case final ReleaseUTXOsCommand cmd:
      return _reservations.releaseMany(currentState, cmd);
    case final ReserveUTXOCommand cmd:
      return _reservations.reserve(currentState, cmd);
    case final ReleaseUTXOCommand cmd:
      return _reservations.release(currentState, cmd);
    case final RenewUTXOReservationCommand cmd:
      return _reservations.renew(currentState, cmd);
    case final CleanupExpiredReservationsCommand cmd:
      return _reservations.cleanupExpired(currentState, cmd);
    case final ReconcileDeferredSpendsCommand cmd:
      return _deferred.reconcile(currentState, cmd);
    case final RecordTransactionNetworkStatusCommand cmd:
      return _deferred.recordNetworkStatus(currentState, cmd);
    case final CancelDeferredSpendCommand cmd:
      return _deferred.cancel(currentState, cmd);
    case final ReclaimDeferredSpendCommand cmd:
      return _deferred.reclaim(currentState, cmd, _transactions);
    default:
      throw ArgumentError('Unknown command type: ${command.runtimeType}');
  }
}