# Délégations

> Le hachage et l’arithmétique de corps et de courbe disposent de circuits dédiés. Quels appels du SDK les atteignent, ce qu’ils coûtent, les règles sur leurs opérandes, et les crates embarquées qui y acheminent le code des bibliothèques.

Certains calculs sont bien moins coûteux à prouver avec un circuit conçu pour eux que sous forme de suite d’instructions RISC-V. Apogee les appelle des **délégations**. Une délégation est une famille de circuits qui prouve une fonction d’un cadre de mots en RAM, invoquée par un `ecall`, et le SDK des programmes invités effectue ces appels pour vous derrière des fonctions ordinaires. Vous n’écrivez jamais d’`ecall` vous-même.

## Ce que vous appelez, et ce que cela atteint

| Vous appelez | Délégation | Un appel prouve |
| --- | --- | --- |
| `guest_sdk::keccak256(&[u8]) -> [u8; 32]` | `KECCAK_F` | un tour de keccak-f[1600]; une permutation représente 24 appels, et l’éponge et le bourrage relèvent du code du programme invité |
| `guest_sdk::sha256(&[u8]) -> [u8; 32]` | `SHA256_COMP` | quatre tours de la compression; une compression représente 16 appels |
| `guest_sdk::ec_add`, `ec_mul`, `ec_identity` | `EC_ADD` | un tiers d’une addition complète de points sur secp256k1 ou sur G1 de BN254 |
| `guest_sdk::poseidon2_permute(&mut [u8; 96])` | `POSEIDON2` | une permutation Poseidon2 de largeur 3 sur `Fr` |
| addition, multiplication et inversion de `field::Fr` | `FR_ARITH` | une opération sur `Fr`, sur la cible des programmes invités, sans rien nommer |
| `transcript::poseidon2_permute` | `POSEIDON2` | la même permutation, par la crate transcript |
| `guest_sdk::recursion::mod_mul` sur un `ModMulFrame` | `MOD_MUL` | un `a·b mod m` sur 256 bits, `m` étant l’un des quatre modules d’Ethereum |

Les fonctions sont identiques bit à bit à leurs définitions logicielles. `keccak256` est le Keccak d’Ethereum, pas SHA3-256. `sha256` est conforme à FIPS 180-4. `ec_add` utilise la formule complète de Renes, Costello et Batina (2015, algorithme 7), si bien que le doublement, `P + (−P)`, l’élément neutre et tout `Z` ne demandent aucun cas particulier.

```rust title="Hachage et arithmétique de courbe depuis un programme invité"
use guest_sdk::{ec_mul, keccak256, recursion::SECP256K1_GROUPS, ProjectivePoint};

let digest: [u8; 32] = keccak256(b"blockchain-native");

// A point is homogeneous projective (x = X/Z, y = Y/Z), each coordinate eight
// little-endian u32 limbs below the field modulus. The scalar is eight limbs too.
fn times(p: &ProjectivePoint, k: &[u32; 8]) -> ProjectivePoint {
    ec_mul(&SECP256K1_GROUPS, p, k).expect("EC_ADD is implemented on Apogee")
}
```

## Le code des bibliothèques les atteint aussi

L’espace de travail des programmes invités corrige trois crates pour que leur code appelle les délégations sur la cible des programmes invités, le code amont servant de chemin de repli :

| Crate | Version | Atteint |
| --- | --- | --- |
| `k256` | 0.13.4 | `MOD_MUL` depuis la multiplication des éléments du corps et des scalaires; `EC_ADD` depuis l’addition, l’addition mixte et le doublement de `ProjectivePoint` |
| `ark-ff` | 0.6.0 | `MOD_MUL` depuis la multiplication et l’élévation au carré de Montgomery de BN254, dans ses deux corps |
| `revm-precompile` | 43.0.2 | `SHA256_COMP` pour le précompilé `0x02`; `EC_ADD` pour `0x06` et `0x07` |

Un programme invité qui dépend de ces crates reçoit automatiquement les copies corrigées, par la section `[patch.crates-io]` de `guests/Cargo.toml`. Sans correctif, la multiplication et l’élévation au carré dans le corps de `k256` représentaient à elles seules 44 % des cycles d’un bloc du réseau principal. La récupération de clé à partir d’une signature secp256k1 est du code `k256` ordinaire, que les correctifs transforment en arithmétique déléguée.

## Ce que coûte une délégation

Une famille de délégation ne fait partie d’un programme que si le programme lie l’un de ses shims, et un appel se paie en shards de la hauteur de cette famille :

- **Liée et jamais appelée : rien.** La famille est déclarée et prouve zéro shard.
- **Appelée une fois : un shard entier.** Un shard coûte sa hauteur complète, quel que soit son taux d’occupation.
- **Appelée souvent : très peu par appel.** La preuve d’un shard ne grandit que d’un tour de sumcheck par variable à mesure que sa hauteur augmente.

| Famille | Hauteur | Unité de travail | Appels par unité | Unités par shard |
| --- | --- | --- | --- | --- |
| `KECCAK_F` | `2^18` | keccak-f[1600] | 24 | 10 922 |
| `SHA256_COMP` | `2^18` | une compression | 16 | 16 384 |
| `EC_ADD` | `2^16` | une addition complète | 3 | 21 845 |
| `MOD_MUL` | `2^16` | un `a·b mod m` | 1 | 65 536 |
| `POSEIDON2` | `2^8` | une permutation | 1 | 256 |
| `FR_ARITH` | `2^8` | une opération sur `Fr` | 1 | 256 |

Le prix se paie en mémoire plus qu’en temps : la passe avant d’un shard `KECCAK_F` de `2^18` occupe environ 42 GiB d’éléments du corps, et deux de ces shards en cours de traitement ont fixé le pic du bloc Ethereum mesuré.

## Règles sur les opérandes

- **Des opérandes inférieurs à leur module.** Un opérande de `MOD_MUL` ou d’`EC_ADD` supérieur ou égal au module que désigne son sélecteur n’a pas de preuve : l’exécuteur refuse le cadre par une erreur fatale `DelegationFrame`. La version embarquée de `k256` réduit ses éléments du corps à réduction paresseuse avant l’appel.
- **Les points ne sont pas vérifiés à votre place.** `EC_ADD` prouve l’arithmétique de la formule. Savoir si un point est sur la courbe est l’affaire du code appelant, et un programme invité qui tire des points des données auxiliaires (*advice*) doit poser la question.
- **Chaque opération en plusieurs appels correspond à une seule fonction du SDK.** Une permutation keccak représente 24 appels sur un même cadre, une compression SHA-256 en représente 16, une addition de points 3. Chaque appel prouve sa propre étape, et rien ne refuse des appels passés dans le mauvais ordre : ils calculent autre chose. Utilisez `keccak256`, `sha256` et `ec_add`, qui émettent les appels dans l’ordre, plutôt que les shims bruts.
- **Le statut 72** signifie qu’une délégation a répondu quelque chose que son shim refuse. Sur l’exécuteur d’Apogee lui-même, cela n’arrive pas pour un cadre bien formé.

## Ce qui n’est pas délégué

- Le `MULMOD` de l’EVM avec un module arbitraire, `MODEXP`, BLS12-381, et toute primitive absente du tableau ci-dessus s’exécutent sous forme d’instructions.
- Aucun schéma de signature ni aucun couplage n’est délégué en bloc. La récupération de clé secp256k1, c’est `k256` sur `MOD_MUL` et `EC_ADD`; un couplage BN254, c’est `ark-bn254` sur `MOD_MUL`.
- Une délégation est le cœur d’une opération. Le bourrage, les éponges, les boucles sur les blocs et l’échelle d’une multiplication scalaire relèvent du code du programme invité, prouvé sous forme d’instructions.

Les schémas de signature pour les programmes invités figurent dans la [feuille de route de la v2.0.0](https://apogee.gweb3networks.com/docs/quantum-leap/signatures).

## Une nouvelle délégation en vaut-elle la peine?

Le profileur de cycles chiffre les candidats évidents dans chaque rapport, sous forme de plafond sur les cycles qu’une délégation pourrait éliminer :

```text
removable = max(0, cycles − calls·(4 + 2·frame_words))
```

`cycles` est la part de l’exécution qui revient à la catégorie, `calls` le nombre d’entrées dans les fonctions du candidat, et `4 + 2·frame_words` le shim qu’une délégation laisserait derrière elle : les rangements du cadre, l’`ecall` et les chargements du résultat. La formule ne compte rien pour les shards de la nouvelle famille : traitez-la comme une borne supérieure. [Exécuter et profiler](https://apogee.gweb3networks.com/docs/launch/run#profiler) montre un rapport.

La spécification de chaque délégation, colonne par colonne, se trouve sous [Circuits de délégation](https://apogee.gweb3networks.com/docs/auditors/spec/delegation-circuits).
