# BLK-005: Aggregated Signature Across Bundles

> **Authoritative requirement:** [BLK-005](../NORMATIVE.md#BLK-005)
> **Verification:** [VERIFICATION.md](../VERIFICATION.md)
> **Tracking:** [TRACKING.yaml](../TRACKING.yaml)
> **Spec reference:** [SPEC.md](../../../../resources/SPEC.md) — Section 6

---

## Summary

BLK-005 requires that the `aggregated_signature` field in `BlockGeneratorResult` MUST be the BLS aggregation of all individual bundle signatures for the bundles included in the block. This is a straightforward cryptographic aggregation: the signatures from each included `SpendBundle` are combined using `bls_aggregate()` to produce a single `Signature` value that covers all spends in the block.

This mirrors Chia L1's block construction, where the block header contains a single aggregated BLS signature covering all transactions. On L1, individual transaction signatures are aggregated during block assembly. BLS signature aggregation is a fundamental property of the BLS12-381 scheme: given signatures `sig_1, sig_2, ..., sig_n` over messages `msg_1, msg_2, ..., msg_n`, the aggregated signature can be verified in a single pairing check against all message/public-key pairs, which is more efficient than verifying each signature individually.

The aggregated signature is critical for block validation ([BLK-006](BLK-006.md), [BLK-008](BLK-008.md)). When `validate_block()` runs, it extracts `AGG_SIG_*` conditions from the generator output and verifies them against this aggregated signature. If a bundle is skipped during iteration ([BLK-002](BLK-002.md)), its signature MUST NOT be included in the aggregate. The aggregate MUST include exactly the signatures of the bundles whose spends appear in the generator.

---

## Specification

### Aggregation Rule

```rust
// Pseudocode for signature aggregation inside build_block_generator()
let mut signatures: Vec<Signature> = Vec::new();

for bundle in included_bundles {
    signatures.push(bundle.aggregated_signature.clone());
}

let aggregated_signature = bls_aggregate(&signatures);
```

### BLS Aggregation Properties

The BLS aggregate operation satisfies:

1. **Deterministic**: `aggregate([sig_A, sig_B]) == aggregate([sig_A, sig_B])` always.
2. **Order-independent**: `aggregate([sig_A, sig_B]) == aggregate([sig_B, sig_A])`. The aggregate signature is the same regardless of the order the individual signatures are combined.
3. **Associative**: `aggregate([aggregate([sig_A, sig_B]), sig_C]) == aggregate([sig_A, sig_B, sig_C])`.

### Behavioral Rules

1. **Exact inclusion**: The aggregate MUST include the signature of every bundle whose spends are included in the generator. No more, no less.

2. **Skipped bundles excluded**: If a bundle is skipped due to cost limits ([BLK-002](BLK-002.md)), its signature MUST NOT be part of the aggregate.

3. **BLS library**: The aggregation MUST use `chia-bls::aggregate_verify` or the equivalent aggregation primitive from the `chia-bls` crate. No hand-rolled BLS implementation is permitted.

4. **Empty case**: If zero bundles are included (all skipped), the aggregated signature MUST be the BLS identity element (the point at infinity), which is the correct aggregate of an empty set of signatures.

5. **Single bundle**: If exactly one bundle is included, the aggregated signature equals that bundle's `aggregated_signature` (aggregation of a single element is identity).

### Verification Compatibility

The aggregated signature stored in `BlockGeneratorResult` MUST be verifiable by `validate_block()` ([BLK-006](BLK-006.md)). Specifically, when `validate_block()` extracts `AGG_SIG_*` conditions from the generator output, the domain-separated messages and public keys from those conditions MUST verify against this aggregated signature via `aggregate_verify()`.

---

## Acceptance Criteria

- [ ] `aggregated_signature` in `BlockGeneratorResult` is computed using BLS aggregation from `chia-bls`
- [ ] Building from two signed bundles produces an `aggregated_signature` equal to `bls_aggregate([bundle1.sig, bundle2.sig])`
- [ ] A skipped bundle's signature does not appear in the aggregate (verified by aggregate_verify failure when including the skipped bundle's conditions)
- [ ] Building from a single bundle produces `aggregated_signature == bundle.aggregated_signature`
- [ ] The aggregated signature passes `aggregate_verify` when checked against all `AGG_SIG_*` conditions from the included bundles

---

## Implementation Notes

- The `Signature` type is from `chia-bls` and represents a BLS12-381 G2 element.
- BLS aggregation is computationally cheap (a single elliptic curve point addition per signature). The expensive operation is verification, which involves pairing computations.
- The BLS cache ([BLK-006](BLK-006.md)) can be used during `validate_block()` to avoid re-verifying signature pairings that were already verified during mempool validation. This is why the aggregated signature must be correctly constructed: the cache matches on individual pairing components, not the aggregate.
- When `DONT_VALIDATE_SIGNATURE` is set in `ValidationConfig.flags`, the aggregated signature is still computed and stored but not verified during `validate_block()`. This is useful for testing and syncing.

---

## Verification

### Test Plan

| Test Case | Input | Expected Outcome |
|-----------|-------|------------------|
| Two-bundle aggregation | Bundles A and B with known signatures | `result.aggregated_signature == bls_aggregate([A.sig, B.sig])` |
| Single bundle | One signed bundle | `result.aggregated_signature == bundle.aggregated_signature` |
| Skipped bundle excluded | Bundles [A, B_expensive, C]; B skipped | `result.aggregated_signature == bls_aggregate([A.sig, C.sig])` |
| Aggregate verifies | Build block, extract AGG_SIG conditions | `aggregate_verify(conditions, result.aggregated_signature) == true` |
| Wrong aggregate fails | Manually corrupt the aggregate | `aggregate_verify` returns false |

### Verification Method

- **Simulator test**: Create two spend bundles with `chia-sdk-test::Simulator`, each signing with a known secret key. Build a block from both. Independently compute `bls_aggregate([bundle1.sig, bundle2.sig])` and assert equality with `result.aggregated_signature`.
- **Integration test**: Build a block, then pass it to `validate_block()`. If BLS verification passes, the aggregate was correctly constructed.

---

## Source Citations

- BLS aggregation in Chia: [`chia-bls` crate](https://github.com/Chia-Network/chia_rs/tree/main/crates/chia-bls)
- L1 block header contains aggregated signature: Chia block format
- `AGG_SIG_*` domain separation: [`condition_tools.py:87-97`](https://github.com/Chia-Network/chia-blockchain/blob/main/chia/consensus/condition_tools.py#L87)

---

## References

- [BLK-001](BLK-001.md) — `build_block_generator` entry point
- [BLK-002](BLK-002.md) — Cost-aware iteration (determines which bundles are included)
- [BLK-004](BLK-004.md) — `BlockGeneratorResult` structure (contains `aggregated_signature`)
- [BLK-006](BLK-006.md) — `validate_block` verifies the aggregated signature
- [BLK-008](BLK-008.md) — BLS verification checks
- [VAL-012](../../validation/NORMATIVE.md#VAL-012) — BLS signature verification in `validate_spend_bundle`
