Skip to content
Open
Show file tree
Hide file tree
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
22 changes: 21 additions & 1 deletion constraints-oapi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ A proposer can delegate constraint submission rights by signing a Delegate messa
tags:
- Constraints API
summary: "Endpoint to retrieve the signed constraints for a given slot"
description: "For a given slot, the Relay should only return signed constraints that were signed by the L1 proposer or a gateway that was delegated to by the proposer. The request requires authorization via the `X-Receiver-Signature` header which is a BLS signature over the requested `slot` number and the `X-Receiver-PublicKey` header which is the corresponding BLS public key. If there are restrictions on accessing constraints, the Relay will check the signature against the BLS public keys in `ConstraintsMessage.Receivers[]`.
description: "For a given slot, the Relay should only return signed constraints that were signed by the L1 proposer or a gateway that was delegated to by the proposer. The request requires authorization via the `X-Receiver-Signature` header which is a BLS signature over the requested `slot` number and the `X-Receiver-PublicKey` header which is the corresponding BLS public key. The `X-Receiver-Nonce` header is the nonce used for the signature and the `X-Receiver-SigningId` header is the signing ID used for the signature as part of the [Commit-Boost Signing Spec](https://commit-boost.github.io/commit-boost-client/api/). If there are restrictions on accessing constraints, the Relay will check the signature against the BLS public keys in `ConstraintsMessage.Receivers[]`.
"
parameters:
- name: slot
Expand All @@ -151,6 +151,16 @@ A proposer can delegate constraint submission rights by signing a Delegate messa
required: true
schema:
$ref: "./beacon-apis/types/primitive.yaml#/Pubkey"
- name: X-Receiver-Nonce
in: header
required: true
schema:
$ref: "./beacon-apis/types/primitive.yaml#/Uint64"
- name: X-Receiver-SigningId
in: header
required: true
schema:
$ref: "./beacon-apis/types/primitive.yaml#/Bytes32"
responses:
"200":
description: "OK"
Expand Down Expand Up @@ -283,10 +293,16 @@ components:
properties:
message:
$ref: '#/components/schemas/Delegation'
nonce:
$ref: './beacon-apis/types/primitive.yaml#/Uint64'
signing_id:
$ref: './beacon-apis/types/primitive.yaml#/Bytes32'
signature:
$ref: './beacon-apis/types/primitive.yaml#/Signature'
required:
- message
- nonce
- signing_id
- signature
Delegation:
type: object
Expand Down Expand Up @@ -314,6 +330,10 @@ components:
properties:
message:
$ref: '#/components/schemas/ConstraintsMessage'
nonce:
$ref: './beacon-apis/types/primitive.yaml#/Uint64'
signing_id:
$ref: './beacon-apis/types/primitive.yaml#/Bytes32'
signature:
$ref: "./beacon-apis/types/primitive.yaml#/Signature"
required:
Expand Down
37 changes: 29 additions & 8 deletions specs/builder.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,7 @@ Some nuances:
| Name | Value |
| - | - |
| `DOMAIN_APPLICATION_BUILDER` | `DomainType('0x00000001')` |
| `DOMAIN_APPLICATION_GATEWAY` | TBD |
| `DELEGATION_DOMAIN_SEPARATOR` | `DomainType('0x0044656c')` |
| `SIGNING_DOMAIN` | `Bytes32('0x6d6d6f43719103511efa4f1362ff2a50996cccf329cc84cb410c5e5c7d351d03')` |

#### Constraints parameters

Expand Down Expand Up @@ -91,6 +90,8 @@ class ConstraintsMessage(Container):
```python
class SignedConstraints(Container):
message: ConstraintsMessage
nonce: uint64
signing_id: Bytes32
signature: BLSSignature
```

Expand Down Expand Up @@ -165,10 +166,29 @@ def verify_builder_slot_signature(slot: uint64, builder_pubkey: BLSPubkey, signa

### `verify_constraint_signature`
```python
def verify_constraint_signature(signed_constraints: SignedConstraints) -> bool:
domain = compute_domain(DOMAIN_APPLICATION_GATEWAY)
signing_root = compute_signing_root(signed_constraints.message, domain)
return bls.Verify(signed_constraints.message.delegate, signing_root, signed_constraints.signature)
class PropCommitSigningInfo(Container):
data: Bytes32
pub module_signing_id: Bytes32
pub nonce: uint64
pub chain_id: uint64

class SigningData(Container):
object_root: Bytes32
signing_domain: Bytes32

def verify_constraint_signature(
signed_constraints: SignedConstraints,
signing_id: bytes32,
nonce: uint64,
chainid: uint64
) -> bool:
# note the object root is abi-encoded, not SSZ
object_root = keccak256(abi.encode(signed_constraints.message))

# the rest is SSZ-encoded
comm_info = PropCommitSigningInfo(object_root, signing_id, to_little_endian_bytes32(nonce), to_little_endian_bytes32(chain_id))
signing_data = SigningData(comm_info.hash_tree_root(), SIGNING_DOMAIN)
return BLS.verify(signed_constraints.message.delegate, signing_data.hash_tree_root(), signed_constraints.signature)
```

### `process_constraints`
Expand All @@ -177,7 +197,8 @@ A `SignedConstraints` is considered valid if the following function completes wi
```python
def process_constraints(state: BeaconState,
signed_constraints: SignedConstraints,
delegations: Dict[BLSPubkey, Delegation]) -> bool:
delegations: Dict[BLSPubkey, Delegation],
chainid: uint64) -> bool:
constraints = signed_constraints.message

# Verify delegate has authority from proposer
Expand All @@ -189,7 +210,7 @@ def process_constraints(state: BeaconState,
assert len(constraints.constraints) <= MAX_CONSTRAINTS_PER_SLOT

# Verify constraint signature
assert verify_constraint_signature(signed_constraints)
assert verify_constraint_signature(signed_constraints, signed_constraints.signing_id, signed_constraints.nonce, chainid)

# Verify slot matches delegation
assert constraints.slot == delegation.slot
Expand Down
16 changes: 13 additions & 3 deletions specs/constraints-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ Endpoint for submitting a batch of constraints to the relay. The constraints are
# A signed "bundle" of constraints.
class SignedConstraints(Container):
message: ConstraintsMessage
nonce: uint64
signing_id: Bytes32
signature: BLSSignature

# A "bundle" of constraints for a specific slot.
Expand Down Expand Up @@ -108,8 +110,8 @@ Endpoint for submitting a batch of constraints to the relay. The constraints are

Additional requirements:

- To ensure deterministic behavior for stateful constraints it is required for the ConstraintsMessage.Constraints[] to be processed in the order received.
- The ConstraintsMessage.Receivers[] field contains a list of public keys that are authorized to access these constraints. If this list is empty, the constraints are publicly accessible to anyone.
- To ensure deterministic behavior for stateful constraints it is required for the `ConstraintsMessage.Constraints[]` to be processed in the order received.
- The `ConstraintsMessage.Receivers[]` field contains a list of public keys that are authorized to access these constraints. If this list is empty, the constraints are publicly accessible to anyone.

- **Example**

Expand Down Expand Up @@ -141,6 +143,8 @@ Endpoint for a Proposer to delegate constraint submission rights to a Gateway. T
# A signed delegation
class SignedDelegation(Container):
message: Delegation
nonce: uint64
signing_id: Bytes32
signature: BLSSignature

# A delegation from a proposer to a BLS public key
Expand Down Expand Up @@ -216,6 +220,8 @@ Return the active delegations for the proposer of this slot, if they exist.
"slot": "12345",
"metadata": "0xe9587369b2301d0790347320302cc069b2301d0790347320302cc0943d5a1884560367e8208d920f2e9587369b2301de9587369b2301d0790347320302cc0"
},
"nonce": 1337,
"signing_id": "0x3078313233340000000000000000000000000000000000000000000000000000",
"signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505"
}
]
Expand All @@ -230,7 +236,7 @@ Return the active delegations for the proposer of this slot, if they exist.

### Endpoint: `/constraints/v0/relay/constraints/{slot}`

Returns all signed constraints for a given slot, if they exist. The request requires authorization via the `X-Receiver-Signature` header which is a BLS signature over the requested `slot` number. If there are restrictions on accessing constraints, the Relay will check the signature against the BLS public keys in `ConstraintsMessage.Receivers[]`.
Returns all signed constraints for a given slot, if they exist. The request requires authorization via the `X-Receiver-Signature` header which is a BLS signature over the requested `slot` number. If there are restrictions on accessing constraints, the Relay will check the signature against the BLS public keys in `ConstraintsMessage.Receivers[]`. The `X-Receiver-Signature` will adhere to the [Commit-Boost Signing Spec](https://commit-boost.github.io/commit-boost-client/api/).

- **Method:** `GET`
- **Response:** `SignedConstraints[]`
Expand All @@ -241,6 +247,8 @@ Returns all signed constraints for a given slot, if they exist. The request requ
- `Content-Type: application/json`
- `X-Receiver-Signature: <BLS signature>`
- `X-Receiver-PublicKey: <BLS public key>`
- `X-Receiver-Nonce: <Nonce used for signature>`
- `X-Receiver-SigningId: <Signing ID used for signature>`

- **Example Response**
```json
Expand All @@ -265,6 +273,8 @@ Returns all signed constraints for a given slot, if they exist. The request requ
"0x84e47f2209abcacf57b75a51dafae777f9dd38bc7053d1af526f220a7489a6d3a2753e5f3e8b1cfe39b56f43611df74b"
]
},
"nonce": 1337,
"signing_id": "0x3078313233340000000000000000000000000000000000000000000000000000",
"signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505"
}
]
Expand Down
4 changes: 2 additions & 2 deletions specs/fault-attribution.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,14 +70,14 @@ Fault attribution is very dependent on the *completeness* of `SignedConstraints`
The following modes serve as implementation guidance rather than prescriptive rules. From the perspective of the spec, these modes are indistinguishable—each simply represents a different strategy for leveraging the `ConstraintProofs` field. This is similar to how “optimistic relaying” emerged as an optimization within PBS: adopted in practice but not enshrined in the spec.

### Optimistic relaying
This mode favors optimizes for performance over safety. By omitting heavy proof generation and verification, more time is available to build the block, potentially increasing the value of the block. However, this introduces the possibility that an invalid block is relayed to the Proposer, in which case the Builder should be penalized. The Proposer should decide whether to enable optimistic relaying, i.e., in their `Delegation.metadata` field.
This mode optimizes for performance over safety. By omitting heavy proof generation and verification, more time is available to build the block, potentially increasing the value of the block. However, this introduces the possibility that an invalid block is relayed to the Proposer, in which case the Builder should be penalized. The Proposer should decide whether to enable optimistic relaying, i.e., in their `Delegation.metadata` field.
1. Gateway posts `SignedConstraints` to the Relay.
2. Builder constructs a block that satisfies all the constraints they received. In the `VersionedSubmitBlockRequestWithProofs.proofs` field, the Builder includes their signature over the `SignedContraints` object, effectively attesting that the block complies with the constraints they were given.
3. Relay includes the block in their auction if the Builder's signature is valid over the `SignedConstraints`.
4. Proposer receives the signed header and proposes the block.

### Pessimistic relaying
Conversely, this mode favors optimizes for safety over performance. Builders are required to generate proofs of constraint satisfaction which comes with the associated compute costs. It is advised that this is the default setting that needs to be overriden by Proposers, i.e., in their `Delegation.metadata` field.
Conversely, this mode optimizes for safety over performance. Builders are required to generate proofs of constraint satisfaction which comes with the associated compute costs. It is advised that this is the default setting that needs to be overriden by Proposers, i.e., in their `Delegation.metadata` field.
1. Gateway posts `SignedConstraints` to the Relay.
2. Builder constructs a block that satisfies all the constraints they received. The Builder includes proofs of constraint satisfaction in the `VersionedSubmitBlockRequestWithProofs.proofs` field.
3. Relay includes the block in their auction if the Builder's proofs are valid over the `SignedConstraints`.
Expand Down
61 changes: 45 additions & 16 deletions specs/gateway.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,7 @@ Some nuances:
| Name | Value |
| - | - |
| `DOMAIN_APPLICATION_BUILDER` | `DomainType('0x00000001')` |
| `DOMAIN_APPLICATION_GATEWAY` | TBD |
| `DELEGATION_DOMAIN_SEPARATOR` | `DomainType('0x0044656c')` |
| `SIGNING_DOMAIN` | `Bytes32('0x6d6d6f43719103511efa4f1362ff2a50996cccf329cc84cb410c5e5c7d351d03')` |

#### URC parameters

Expand Down Expand Up @@ -99,6 +98,8 @@ class Delegation(Container):
```python
class SignedDelegation(Container):
message: Delegation
nonce: uint64
signing_id: Bytes32
signature: BLS.G2Point
```

Expand All @@ -123,6 +124,8 @@ class ConstraintsMessage(Container):
```python
class SignedConstraints(Container):
message: ConstraintsMessage
nonce: uint64
signing_id: Bytes32
signature: BLSSignature
```

Expand Down Expand Up @@ -167,12 +170,31 @@ def is_eligible_for_delegation(state: BeaconState, validator: Validator) -> bool

#### verify_delegation_signature
```python
def verify_delegation_signature(signed_delegation: SignedDelegation) -> bool:
pubkey = signed_delegation.message.proposer
# note abi-encoded, not SSZ
message = abi.encode(signed_delegation.message)
signing_root = keccak256(abi.encode_packed(DELEGATION_DOMAIN_SEPARATOR, message))
return bls.Verify(pubkey, signing_root, signed_delegation.signature)
class PropCommitSigningInfo(Container):
data: Bytes32
pub module_signing_id: Bytes32
pub nonce: uint64
pub chainid: uint64

class SigningData(Container):
object_root: Bytes32
signing_domain: Bytes32

def verify_delegation_signature(
signed_delegation: SignedDelegation,
signing_id: Bytes32,
nonce: uint64,
chainid: uint64
) -> bool:
pubkey = signed_delegation.message.proposer

# note the object root is abi-encoded, not SSZ
object_root = keccak256(abi.encode(IRegistry.MessageType.Delegation, signed_delegation.message))

# the rest is SSZ-encoded
comm_info = PropCommitSigningInfo(object_root, signing_id, to_little_endian_Bytes32(nonce), to_little_endian_Bytes32(chainid))
signing_data = SigningData(comm_info.hash_tree_root(), SIGNING_DOMAIN)
return BLS.verify(pubkey, signing_data.hash_tree_root(), signed_delegation.signature)
```

#### process_delegation
Expand All @@ -182,7 +204,8 @@ A `delegation` is considered valid if the following function completes without r
def process_delegation(state: BeaconState,
signed_delegation: SignedDelegation,
delegations: Dict[BLSPubkey, Delegation],
current_timestamp: uint64):
current_timestamp: uint64,
chainid: uint64):
signature = signed_delegation.signature
delegation = signed_delegation.message

Expand All @@ -197,7 +220,7 @@ def process_delegation(state: BeaconState,
assert is_eligible_for_delegation(state, validator)

# Verify delegation signature
assert verify_delegation_signature(signed_delegation)
assert verify_delegation_signature(signed_delegation, signed_delegation.signing_id, signed_delegation.nonce, chainid)
```

## Issuing commitments
Expand Down Expand Up @@ -251,7 +274,10 @@ class get_signed_constraints(
proposer: BLSPubkey,
slot: int,
receivers: List[BLSPubkey],
privkey: int) -> SignedConstraints:
privkey: int,
signing_id: Bytes32
nonce: uint64.
chainid: uint64) -> SignedConstraints:

message = ConstraintsMessage(
proposer: proposer,
Expand All @@ -260,12 +286,15 @@ class get_signed_constraints(
constraints: constraints,
receivers: receivers)

# note abi-encoded, not SSZ
message = abi.encode(message)
signing_root = keccak256(abi.encode_packed(DOMAIN_APPLICATION_GATEWAY, message))
signature = bls.Sign(privkey, signing_root)
# note the object root is abi-encoded, not SSZ
object_root = keccak256(abi.encode(message))

# the rest is SSZ-encoded
comm_info = PropCommitSigningInfo(object_root, signing_id, to_little_endian_Bytes32(nonce), to_little_endian_Bytes32(chainid))
signing_data = SigningData(comm_info.hash_tree_root(), SIGNING_DOMAIN)
signature = BLS.sign(privkey, signing_data.hash_tree_root())

return SignedConstraints(message: message, signature: signature)
return SignedConstraints(message: message, nonce: nonce, signing_id: signing_id, signature: signature)
```

### Disseminating constraints
Expand Down
Loading