7 · Validate the BEEF
Bob has been handed a BEEF. Before he counts it as paid, his wallet checks it without trusting Alice or a server:
- each merkle proof in the BEEF against the block headers Bob’s wallet synced itself;
- that the outputs pay the invoice’s addresses;
- that the amount matches the invoice.
If it’s valid, libspiffy broadcasts it through ARC (Arcade, here).
// Step 7: Bob validates the BEEF he was handed, which also broadcasts it. try { final verdict = await coordinator.ask(ValidateBEEFCommand( walletId: 'bob', beefHex: hexEncode(payment.beefBytes), invoiceId: invoice.invoiceId, )); print('Bob: valid=${verdict.valid} broadcasted=${verdict.broadcasted} ' 'networkStatus=${verdict.networkStatus}'); } on CoordinatorFailure catch (e) { print('Bob refused the payment: ${e.message}'); rethrow; }Bob: valid=true broadcasted=true networkStatus=SEEN_ON_NETWORK [bob] invoice d2fb82ed-424b-4477-9a65-fc8e9394947a paid: 100000 sats in c5013d51…ValidateBEEFCommand takes the BEEF hex-encoded. Its reply is a BEEFValidationResultEvent. An invalid BEEF is a failed request, so ask throws CoordinatorFailure with the reason, and the try/catch above handles it. When the BEEF is valid, the reply carries:
broadcastedandnetworkStatus: what ARC said when the payment was sent.broadcastError: why the broadcast failed, when it did.
If a proof refers to a block whose header Bob’s wallet doesn’t hold yet, the BEEF can’t be checked yet either. ask then throws, and the failure’s event (e.event, a BEEFValidationResultEvent) has awaitingHeader set to true. The wallet keeps the payment and validates it once the header arrives. That later verdict carries no request id, so follow it with coordinator.on<BEEFValidationResultEvent>(walletId: 'bob').
The line invoice … paid comes from an InvoicePaidEvent, which libspiffy emits once ARC reports the network holds the payment. You print it from the listener you add in step 8.