onCommandFailure method
Send error responses when command processing fails Only active when aggregate is used as an actor in the actor system
Implementation
@override
Future<void> onCommandFailure(Command command, dynamic error) async {
await super.onCommandFailure(command, error);
// The events behind a prepared success reply were not journaled.
_repliesAwaitingPersist.remove(command.commandId);
// Key material was written before the WalletCreatedEvent; if the event
// could not be persisted, take the secrets back out (best effort).
final pendingWalletId = _keyMaterialAwaitingPersist.remove(command.commandId);
if (pendingWalletId != null) {
_log.warning('Wallet $pendingWalletId: creation failed after key material '
'was stored; removing it again: $error');
await _keys.removeKeyMaterial(pendingWalletId, cause: error ?? 'unknown');
}
// Only send responses if we're running in an actor system
if (!_isInActorSystem()) {
return;
}
// Use captured sender keyed by command ID (same reasoning as onCommandProcessed)
final sender = _capturedSenders[command.commandId];
if (sender == null) {
return;
}
if (!_failureReplied.add(command.commandId)) {
return;
}
final errorMessage = error.toString();
if (command is CreateWalletCommand) {
sender.tell(WalletCreatedResponse(
walletId: command.walletId,
rootAddress: '',
success: false,
error: errorMessage,
));
} else if (command is GenerateAddressCommand) {
sender.tell(AddressGeneratedResponse(
walletId: command.walletId,
address: '',
derivationIndex: 0,
chain: AddressChain.receive,
success: false,
error: errorMessage,
metadata: command.metadata, // Pass through metadata even on error
));
} else if (command is ReserveUTXOCommand) {
sender.tell(UTXOReservedResponse(
walletId: command.walletId,
utxoKey: command.utxoKey,
reservedByTxId: command.reservedByTxId,
success: false,
error: errorMessage,
));
} else if (command is ReserveUTXOsCommand) {
sender.tell(UTXOReservedResponse(
walletId: command.walletId,
utxoKey: command.utxoKeys.join(','),
reservedByTxId: command.reservationId,
success: false,
error: errorMessage,
));
} else if (command is BuildFundingTransactionCommand) {
sender.tell(FundingTransactionBuiltResponse(
walletId: command.walletId,
correlationId: command.correlationId,
channelId: command.channelId,
fundingTxHex: '',
fundingTxId: '',
fundingOutputIndex: 0,
success: false,
error: errorMessage,
));
} else if (command is SignTransactionCommand) {
sender.tell(TransactionSignedResponse(
walletId: command.walletId,
txid: command.transactionId,
signedHex: '',
success: false,
error: errorMessage,
));
} else if (command is SignMultisigTransactionCommand) {
sender.tell(MultisigTransactionSignedResponse(
walletId: command.walletId,
txid: '',
originalTransactionId: command.transactionId,
signedHex: '',
signatureHex: '',
success: false,
error: errorMessage,
));
} else if (command is SignInputCommand) {
sender.tell(InputSignedResponse(
walletId: command.walletId,
commandId: command.commandId,
inputIndex: command.inputIndex,
signatureHex: '',
publicKeyHex: '',
success: false,
error: errorMessage,
));
} else if (command is CancelDeferredSpendCommand) {
sender.tell(DeferredSpendCancelledResponse(
walletId: command.walletId,
txid: command.txid,
success: false,
error: errorMessage,
));
} else if (command is CompleteDeferredSpendCommand) {
sender.tell(DeferredSpendCompletedResponse(
walletId: command.walletId,
txid: command.txid,
completedTxid: command.completedTxid,
success: false,
error: errorMessage,
));
} else if (command is ReclaimDeferredSpendCommand) {
sender.tell(DeferredSpendReclaimedResponse(
walletId: command.walletId,
txid: command.txid,
reclaimTxid: command.reclaimTxid,
success: false,
error: errorMessage,
));
} else if (command is AddWatchAddressCommand) {
sender.tell(WatchAddressAddedResponse(
walletId: command.walletId,
address: command.address,
success: false,
error: errorMessage,
));
} else if (command is RecordDelegatedAddressesCommand) {
sender.tell(DelegatedAddressesRecordedResponse(
walletId: command.walletId,
success: false,
error: errorMessage,
));
} else if (command is RecordType42AddressesCommand) {
sender.tell(Type42AddressesRecordedResponse(walletId: command.walletId, success: false, error: errorMessage));
} else if (command is DeriveType42DestinationCommand) {
sender.tell(Type42DestinationDerivedResponse(walletId: command.walletId, success: false, error: errorMessage));
} else if (command is IssueAnchorKeyCommand || command is SignWithAnchorKeyCommand) {
sender.tell(AnchorKeyResponse(walletId: (command as WalletCommand).walletId, success: false, error: errorMessage));
} else if (command is LookupType42AddressesCommand) {
sender.tell(Type42AddressesResponse(walletId: command.walletId, success: false, error: errorMessage));
} else if (command is Brc100KeyOperationCommand) {
sender.tell(Brc100KeyOperationResponse(walletId: command.walletId, success: false, error: errorMessage));
} else {
// Every other command: the aggregate has no reply of its own for it,
// so the failure is reported in the shape every caller understands
// (bead libspiffy-kl4i).
sender.tell(WalletCommandFailed(
walletId: command is WalletCommand ? command.walletId : aggregateId,
request: command.runtimeType.toString(),
error: errorMessage,
));
}
}