Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
222 changes: 222 additions & 0 deletions core/cap-0080.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
## Preamble

```
CAP: 0080
Title: Host functions for efficient ZK BN254 use cases
Working Group:
Owner: Siddharth Suresh <@sisuresh>
Authors: Jay Geng <@jayz22>, Siddharth Suresh <@sisuresh>
Consulted: Tomer Weller <@tomerweller>
Status: Draft
Created: 2026-01-21
Discussion: https://github.com/orgs/stellar/discussions/1826
Protocol version: 26
```

## Simple Summary

BN254 MSM (Multi-Scalar Multiplication) and modular arithmetic are used in a variety of ZK applications. Adding host support for these will greatly improve the performance of these use cases.

## Working Group

As described in the preamble section.

## Motivation

Stellar has host support for the BN254 functions available on the EVM (G1 Add, G1 Mul, and Pairing), but there are use cases that require additional functionality.

Contracts need modular arithmetic operations on scalar field elements (Fr) and cannot afford to implement these on the guest side. Adding host functions for add, sub, mul, pow, and inv enables these use cases.

For use cases that require many G1 additions and scalar multiplications, the cost of repeatedly converting points and scalars between their external encoding and the internal representation can be expensive. Each call to `bn254_g1_add` or `bn254_g1_mul` requires this conversion. With MSM, the conversion happens only once at the beginning, all intermediate operations occur in internal form, and only the final result is converted back. This significantly reduces the overall cost.


### Goals Alignment
This CAP is aligned with the following Stellar Network Goals:

* The Stellar Network should run at scale and at low cost to all participants of the network.

## Abstract
Six new host functions are proposed here.

## Specification

### New host functions
```
{
"export": "r",
"name": "bn254_g1_msm",
"args": [
{ "name": "vp", "type": "VecObject" },
{ "name": "vs", "type": "VecObject" }
],
"return": "BytesObject",
"docs": "Performs multi-scalar-multiplication (inner product) on a vector of BN254 G1 points (`Vec<BytesObject>`) by a vector of scalars (`Vec<U256Val>`), and returns the resulting G1 point in 64-byte uncompressed format.",
"min_supported_protocol": 26
},
{
"export": "s",
"name": "bn254_fr_add",
"args": [
{ "name": "lhs", "type": "U256Val" },
{ "name": "rhs", "type": "U256Val" }
],
"return": "U256Val",
"docs": "Performs addition `(lhs + rhs) mod r` between two BN254 scalar elements (Fr), where r is the subgroup order",
"min_supported_protocol": 26
},
{
"export": "t",
"name": "bn254_fr_sub",
"args": [
{ "name": "lhs", "type": "U256Val" },
{ "name": "rhs", "type": "U256Val" }
],
"return": "U256Val",
"docs": "Performs subtraction `(lhs - rhs) mod r` between two BN254 scalar elements (Fr), where r is the subgroup order",
"min_supported_protocol": 26
},
{
"export": "u",
"name": "bn254_fr_mul",
"args": [
{ "name": "lhs", "type": "U256Val" },
{ "name": "rhs", "type": "U256Val" }
],
"return": "U256Val",
"docs": "Performs multiplication `(lhs * rhs) mod r` between two BN254 scalar elements (Fr), where r is the subgroup order",
"min_supported_protocol": 26
},
{
"export": "v",
"name": "bn254_fr_pow",
"args": [
{ "name": "lhs", "type": "U256Val" },
{ "name": "rhs", "type": "U64Val" }
],
"return": "U256Val",
"docs": "Performs exponentiation of a BN254 scalar element (Fr) with a u64 exponent i.e. `lhs.exp(rhs) mod r`, where r is the subgroup order",
"min_supported_protocol": 26
},
{
"export": "w",
"name": "bn254_fr_inv",
"args": [
{ "name": "lhs", "type": "U256Val" }
],
"return": "U256Val",
"docs": "Performs inversion of a BN254 scalar element (Fr) modulo r (the subgroup order)",
"min_supported_protocol": 26
}
```

### XDR changes
```
diff --git a/Stellar-contract-config-setting.x b/Stellar-contract-config-setting.x
index 9a95937da..f1b8a3a78 100644
--- a/Stellar-contract-config-setting.x
+++ b/Stellar-contract-config-setting.x
@@ -291,7 +291,9 @@ enum ContractCostType {
// Cost of performing BN254 scalar element exponentiation
Bn254FrPow = 83,
// Cost of performing BN254 scalar element inversion
Copy link

Copilot AI Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is an extra space before the comment. This line should start with four spaces for indentation, followed by two forward slashes, one space, and then the comment text, consistent with the formatting on line 115.

Suggested change
// Cost of performing BN254 scalar element inversion
// Cost of performing BN254 scalar element inversion

Copilot uses AI. Check for mistakes.
- Bn254FrInv = 84
+ Bn254FrInv = 84,
+ // Cost of performing BN254 G1 multi-scalar multiplication (MSM)
+ Bn254G1Msm = 85
};

struct ContractCostParamEntry {
```

### Semantics

#### Field and groups
See [CAP-0074](./cap-0074.md#field-and-groups) for definitions of the BN254 fields and groups.

#### New host functions introduced

##### `bn254_g1_msm`

**Description**: perform multi-scalar-multiplication (MSM) in G1.

**Cost**: includes decoding of the G1 vector (`Bn254DecodeFp`), converting `fr` from `U256` (`Bn254FrFromU256`), the MSM operation `Bn254G1Msm`, converting the point from projective to affine (`Bn254G1ProjectiveToAffine`), and encoding of the resulting G1 point (`Bn254EncodeFp`).

**Error condition**:
1. if the two vectors have different lengths
2. if the length of either vector is zero
3. if any point in the G1 points vector does not decode into a valid G1 point or does not conform to the specified encoding standard:
- Bytes length is not equal to 64
- The point is compressed
- The input point does not belong on the G1 curve

##### `bn254_fr_add`

**Description**: performs addition `(lhs + rhs) mod r` between two BN254 scalar elements (Fr).

**Cost**: conversion of fr from U256 (`Bn254FrFromU256`), scalar addition `Bn254FrAddSub`, and conversion back to U256 (`Bn254FrToU256`).

**Error condition**: None

##### `bn254_fr_sub`

**Description**: performs subtraction `(lhs - rhs) mod r` between two BN254 scalar elements (Fr).

**Cost**: conversion of fr from U256 (`Bn254FrFromU256`), scalar subtraction `Bn254FrAddSub`, and conversion back to U256 (`Bn254FrToU256`).

**Error condition**: None

##### `bn254_fr_mul`

**Description**: performs multiplication `(lhs * rhs) mod r` between two BN254 scalar elements (Fr).

**Cost**: conversion of fr from U256 (`Bn254FrFromU256`), scalar multiplication `Bn254FrMul`, and conversion back to U256 (`Bn254FrToU256`).

**Error condition**: None

##### `bn254_fr_pow`

**Description**: performs exponentiation `lhs.exp(rhs) mod r` between a BN254 scalar element (Fr) and a u64 exponent.

**Cost**: conversion of fr from U256 (`Bn254FrFromU256`), scalar exponentiation `Bn254FrPow`, and conversion back to U256 (`Bn254FrToU256`).

**Error condition**: None

##### `bn254_fr_inv`

**Description**: performs inversion of a BN254 scalar element (Fr).

**Cost**: conversion of fr from U256 (`Bn254FrFromU256`), scalar inversion `Bn254FrInv`, and conversion back to U256 (`Bn254FrToU256`).

**Error condition**: if the provided input `fr` is zero.

#### New metering `CostType` introduced

- `Bn254G1Msm` - Cost of performing BN254 G1 multi-scalar multiplication (MSM). Type: linear w.r.t the length of the input vectors.

## Design Rationale

### Adding BN254 specific host functions instead of more general ones.

The BN254 specific modular arithmetic functions (add, sub, mul, pow, inv) were chosen because they map directly to operations provided by the Arkworks library, which is used in the host implementation. This direct mapping simplifies metering since each host function corresponds to a well-defined Arkworks operation with predictable performance characteristics.

The alternative would be to add host functions that take the modulus as an input, but the metering and implementation would be more complex. We also already have BLS12-381 specific arithmetic functions, so the BN254 ones bring the two closer to feature parity.

If we think the general functions will be helpful in the future, we can add those in later.

## Protocol Upgrade Transition
The proposed host functions will become available in protocol 26.

### Backwards Incompatibilities
This CAP does not introduce any backward incompatibilities.

### Resource Utilization
The only new cost type is for G1 MSM, which we will calibrate.

## Security Concerns
- Proper metering to avoid a Denial of Service.

## Test Cases
TODO

## Implementation
TODO
Copy link

Copilot AI Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Test Cases section contains only "TODO" without any concrete test cases. Before this CAP can be finalized, this section should include specific test cases covering the new host functions, including edge cases for error conditions (e.g., zero-length vectors for MSM, zero input for inversion, mismatched vector lengths).

Suggested change
TODO
This section outlines test cases that should be implemented at the host-function level and, where applicable, at the Soroban contract level to validate the behavior of the new BN254 host functions and their metering.
Unless otherwise specified, “success” means the host function returns `Ok` with the expected value, and “failure” means the host function returns an error code defined in the Soroban host for invalid arguments or arithmetic errors.
### BN254 field operations (`add`, `sub`, `mul`, `pow`, `inv`)
#### Happy-path arithmetic
1. **Addition within field**
- Input: Two valid BN254 field elements `a`, `b` strictly less than the BN254 modulus.
- Example: `a = 1`, `b = 2`.
- Expected: `add(a, b)` succeeds and returns the canonical encoding of `3 mod p`.
2. **Addition with wraparound**
- Input: `a = p - 1`, `b = 2` (where `p` is the BN254 field modulus), both given in canonical encoding.
- Expected: `add(a, b)` succeeds and returns the canonical encoding of `1 mod p`.
3. **Subtraction within field**
- Input: `a = 5`, `b = 3`.
- Expected: `sub(a, b)` succeeds and returns the canonical encoding of `2 mod p`.
4. **Subtraction with underflow**
- Input: `a = 3`, `b = 5`.
- Expected: `sub(a, b)` succeeds and returns the canonical encoding of `p - 2`.
5. **Multiplication**
- Input: `a = 2`, `b = 3`.
- Expected: `mul(a, b)` succeeds and returns the canonical encoding of `6 mod p`.
6. **Exponentiation with small exponents**
- Input: `base = 5`, `exp = 0`.
- Expected: `pow(base, 0)` succeeds and returns `1` (multiplicative identity).
- Input: `base = 5`, `exp = 1`.
- Expected: `pow(base, 1)` succeeds and returns `base`.
- Input: `base = 5`, `exp = 2`.
- Expected: `pow(base, 2)` succeeds and returns canonical encoding of `25 mod p`.
7. **Inversion of non-zero element**
- Input: `x = 2`.
- Expected: `inv(x)` succeeds and returns a value `y` such that `mul(x, y)` returns `1`.
#### Error handling and invalid inputs for field operations
8. **Inversion of zero**
- Input: `x = 0` (canonical zero element).
- Expected: `inv(x)` fails with an appropriate error indicating inversion of zero is not defined.
9. **Non-canonical encoding: value equal to modulus**
- Input: `x = p` encoded as a 32-byte or 64-byte value (per the CAP’s serialization rules) that is not strictly less than `p`.
- Expected: Any field operation (`add`, `sub`, `mul`, `pow`, `inv`) using `x` fails with an “invalid field element” error.
10. **Out-of-range encoding: value greater than modulus**
- Input: `x = p + 1` encoded as a field element.
- Expected: Any field operation using `x` fails with an “invalid field element” error.
11. **Incorrect length encodings**
- Input: Byte array that is shorter or longer than the required field element length.
- Expected: Operation fails with “invalid encoding length” or equivalent error.
12. **Power with large exponent**
- Input: `base` is a valid non-zero field element, `exp` is a large positive integer (e.g., 256‑bit number).
- Expected: Operation succeeds, respects metering limits, and returns a correctly reduced result.
### BN254 G1 multi-scalar multiplication (MSM)
These tests assume there is a host function that takes:
- A vector of BN254 scalars.
- A vector of BN254 G1 points.
- And returns a BN254 G1 point representing the MSM result.
#### Happy-path MSM
13. **Single scalar-point pair**
- Input: Scalars = `[s1]`, Points = `[P1]`, where `s1` and `P1` are valid.
- Expected: Result equals `s1 * P1` (scalar multiplication) encoded canonically.
14. **Multiple scalar-point pairs**
- Input: Scalars = `[s1, s2, s3]`, Points = `[P1, P2, P3]`, all valid.
- Expected: Result equals `s1 * P1 + s2 * P2 + s3 * P3` in BN254 G1.
15. **Including identity point**
- Input: Scalars = `[s1, s2]`, Points = `[P1, O]`, where `O` is the encoded point-at-infinity.
- Expected: Result equals `s1 * P1 + s2 * O = s1 * P1`.
16. **Zero scalar**
- Input: Scalars = `[0, s2]`, Points = `[P1, P2]`.
- Expected: Result equals `0 * P1 + s2 * P2 = s2 * P2`.
#### Edge cases and error conditions for MSM
17. **Zero-length vectors**
- Input: Scalars = `[]`, Points = `[]`.
- Expected: Host function fails with an error indicating empty input not allowed, or returns the identity point if the CAP specifies that as valid behavior. The CAP’s intended behavior must be explicitly tested.
18. **Mismatched vector lengths (more scalars than points)**
- Input: Scalars = `[s1, s2]`, Points = `[P1]`.
- Expected: Host function fails with an error indicating “length mismatch”.
19. **Mismatched vector lengths (more points than scalars)**
- Input: Scalars = `[s1]`, Points = `[P1, P2]`.
- Expected: Host function fails with an error indicating “length mismatch”.
20. **Invalid scalar encoding**
- Input: Scalars vector contains an element that is:
- Out of range (≥ scalar field modulus), or
- Incorrect length.
- Expected: MSM host function fails with an “invalid scalar” error; no partial result is returned.
21. **Invalid point encoding**
- Input: Points vector contains:
- A malformed encoding (wrong length, invalid compression flag), or
- A point not on the BN254 curve (fails curve equation check).
- Expected: MSM host function fails with an “invalid point” error; no partial result is returned.
22. **All-zero scalars**
- Input: Scalars = `[0, 0, 0]`, Points = `[P1, P2, P3]`.
- Expected: Result equals the identity point (point-at-infinity).
23. **Duplicated points**
- Input: Scalars = `[s, s]`, Points = `[P, P]`.
- Expected: Result equals `(s + s) * P`; helps verify correctness of addition vs. repeated scalar multiplication.
24. **Maximum-length vectors within metering budget**
- Input: Scalars and Points vectors filled to the maximum allowed length before hitting metering or argument-size limits.
- Expected: Operation succeeds, is metered linearly in the vector length (`Bn254G1Msm` cost type), and returns a valid point.
25. **Exceeding maximum allowable vector length**
- Input: Scalars and Points vectors longer than any documented or implementation-enforced limit (if such limits exist).
- Expected: Host function fails with an appropriate “too many elements” or similar error, and metering charges must not exceed documented bounds for rejected calls.
### Metering and protocol behavior
26. **Cost type `Bn254G1Msm` linearity**
- Input: Invoke the MSM host function with vectors of length `n` and `2n` where both are well within limits and otherwise identical in structure.
- Expected: Measured cost for the `2n` call is approximately double (within calibration tolerances) the cost for the `n` call, confirming linear metering with respect to vector length.
27. **Failure does not lead to unbounded cost**
- Input: Construct MSM calls that will fail early due to:
- Invalid scalar encoding.
- Invalid point encoding.
- Mismatched vector lengths.
- Expected: The host function aborts with an error and charges only the work performed up to validation failure, complying with metering guarantees (no unbounded or unexpected cost).
28. **Protocol version gating**
- Input: Attempt to invoke the new BN254 host functions in:
- A ledger with protocol version `< 26`.
- A ledger with protocol version `≥ 26`.
- Expected:
- For `< 26`: Calls fail with “unknown host function” or equivalent error.
- For `≥ 26`: Calls behave according to the tests above.
### Soroban contract-level tests
29. **Contract using BN254 field ops**
- Implement a simple Soroban contract that:
- Accepts two field elements as inputs.
- Calls the BN254 `add`, `sub`, and `mul` host functions.
- Returns the triple `(a + b, a - b, a * b)`.
- Tests:
- Valid inputs hit the happy-path arithmetic test expectations.
- Invalid encodings propagate host errors as contract failures.
30. **Contract performing MSM verification**
- Implement a Soroban contract that:
- Takes a vector of scalars and points and a claimed result point.
- Uses the MSM host function to recompute the result and compares it to the claimed point.
- Tests:
- For correct inputs, verification returns success.
- For modified scalar/point/result, verification fails.
- Edge cases (zero-length vectors, mismatched lengths, invalid encodings) cause the contract to fail with propagated host errors.
31. **State-insensitive determinism**
- Input: Invoke the same contract methods (field ops and MSM) multiple times across different ledgers but with identical inputs.
- Expected: Results are deterministic and identical across runs, with no dependence on ledger state beyond protocol version gating.
These test cases should be implemented in the core protocol test suite and in Soroban integration tests to ensure that the new BN254 host functions are correct, robust against invalid inputs, and properly metered.

Copilot uses AI. Check for mistakes.
Loading