Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Shutter: Update *decryption_identities_signature* functions #48

Merged
merged 7 commits into from
Jul 23, 2024
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
49 changes: 31 additions & 18 deletions shutter/low-level.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,10 @@ def make_decryption_key_shares_message(
) -> DecryptionKeyShares:
shares = [
KeyShare(
identity=tx.identity,
identity=compute_identity(tx.identity_preimage),
share=compute_decryption_key_share(
eon_secret_key_share,
tx.identity
tx.identity_preimage
),
)
for tx in txs
Expand Down Expand Up @@ -129,6 +129,7 @@ def check_decryption_key_shares_message(
instance_id=key_shares_message.instanceID,
eon=key_shares_message.eon,
slot=key_shares_message.slot,
tx_pointer=key_shares_message.txPointer,
identities=[share.identity for share in key_shares_message.shares],
signature=key_shares_message.signature,
keyper_address=keypers[key_shares_message.keyperIndex],
Expand Down Expand Up @@ -226,6 +227,7 @@ def check_decryption_keys_message(keys_message: DecryptionKeys, eon: uint64, eon
instance_id=keys_message.instanceID,
eon=keys_message.eon,
slot=keys_message.slot,
tx_pointer=keys_message.txPointer,
identities=[key.identity for key in keys_message.keys],
signature=signature,
keyper_address=keypers[signer_index],
Expand All @@ -247,10 +249,10 @@ Registered validators subscribe to `DecryptionKeys` messages from keypers on the

If a registered validator is selected as the block proposer for slot `slot`, they hold off on producing a block until they receive a valid `DecryptionKeys` message `keys_message` where `keys_message.slot == slot`. If no such message is received up until the end of `slot`, the proposer proposes no block.

Once `keys_message` is received, the validator fetches those `TransactionSubmitted` events `tx_submitted_event` from the sequencer contract that, for any `key` in `keys_message.keys`, fulfill.
Once `keys_message` is received, the validator fetches those `TransactionSubmitted` events `tx_submitted_event` from the sequencer contract that, for any `key` in `keys_message.keys`, fulfill

- `e.args.eon == keys_message.eon` and
- `compute_identity(e.args.identityPrefix, keys_message.sender) == key.identity`.
- `compute_identity(compute_identity_preimage(e.args.identityPrefix, keys_message.sender)) == key.identity`.

The events are fetched in the order the events were emitted. For each `tx_submitted_event` with corresponding `key`, the validator first computes `encrypted_transaction = decode_encrypted_message(e.args.encryptedTransaction)` and then `decrypted_transaction = decrypt(encrypted_transaction, key.key)`. If any of the functions fails, they skip `tx_submitted_event`. The decrypted transactions are appended to a list `decrypted_transactions` in the same order the events are fetched.

Expand All @@ -276,7 +278,7 @@ The server exposes a method `eth_sendRawTransaction` that behaves differently to

If the sender of `tx` has insufficient funds to pay the transaction fee or if the sender's nonce does not match the account nonce, the server responds with an error with code `-32000`.

Upon receiving the request, the server seeks the eon key `eon_key = getEonKey(eon)` with `eon = keyperSetManager.getKeyperSetIndexBySlot(slot + keyper_set_change_lookahead)` where `slot` is the current slot number. It also generates two random 32 byte strings `sigma` and `identity_prefix` using a cryptographically secure random number generator. Based on these values, it computes `encrypted_transaction = encrypt(b, identity, eon_key, sigma)` with `identity = compute_identity(identity_prefix, address)`.
Upon receiving the request, the server seeks the eon key `eon_key = getEonKey(eon)` with `eon = keyperSetManager.getKeyperSetIndexBySlot(slot + keyper_set_change_lookahead)` where `slot` is the current slot number. It also generates two random 32 byte strings `sigma` and `identity_prefix` using a cryptographically secure random number generator. Based on these values, it computes `encrypted_transaction = encrypt(b, identity, eon_key, sigma)` with `identity = compute_identity(compute_identity_preimage(identity_prefix, address))`.

Finally, the server creates a transaction `submit_tx` which calls `ISequencer(SEQUENCER_ADDRESS).submitEncryptedTransaction(eon, identity_prefix, address, encryptedTransaction, tx.gasLimit)`, sets gas limit and nonce as needed and chooses gas price parameters appropriate to the current network conditions. If the account identified by `address` has insufficient funds, it returns an error with code`-32603`. Otherwise, it signs `submit_tx` with `private_key`, broadcasts it to the network, and returns the hex encoded hash of `tx` to the user.

Expand Down Expand Up @@ -314,7 +316,7 @@ class SequencedTransaction:
eon: uint64
encrypted_transaction: bytes
gas_limit: int
identity: G1
identity_preimage: bytes

def get_next_transactions(state: BeaconState, eon: int, tx_pointer: int) -> Sequence[SequencedTransaction]:
events = get_events(state, SEQUENCER_ADDRESS)
Expand All @@ -333,7 +335,7 @@ def get_next_transactions(state: BeaconState, eon: int, tx_pointer: int) -> Sequ
eon=event.args.eon,
encrypted_transaction=event.args.encryptedTransaction,
gas_limit=event.args.gasLimit,
identity=compute_identity(event.args.identityPrefix, event.args.sender),
identity_preimage=compute_identity_preimage(event.args.identityPrefix, event.args.sender),
)
txs.append(tx)
total_gas += event.args.gasLimit
Expand Down Expand Up @@ -682,8 +684,8 @@ def invert(x: int) -> int:
### Key Generation

```python
def compute_decryption_key_share(eon_secret_key_share: int, identity: G1) -> G1:
return g1_scalar_mult(identity, eon_secret_key_share)
def compute_decryption_key_share(eon_secret_key_share: int, identity_preimage: bytes) -> G1:
return g1_scalar_mult(compute_identity(identity_preimage), eon_secret_key_share)

def compute_decryption_key(keyper_indices: Sequence[int], shares: Sequence[G1]) -> G1:
assert len(keyper_indices) == len(shares)
Expand Down Expand Up @@ -726,8 +728,13 @@ def compute_c3(message_blocks: Sequence[Block], sigma: Block) -> Sequence[Block]
keys = compute_block_keys(sigma, len(message_blocks))
return [xor_blocks(key, block) for key, block in zip(keys, message_blocks)]

def compute_identity(prefix: bytes, sender: bytes) -> G1:
return hash1(prefix + sender)
def compute_identity_preimage(prefix: bytes, sender: Address): bytes:
return prefix + sender

def compute_identity(identity_preimage: bytes) -> G1:
h = keccak256(identity_preimage)
i = int.from_bytes(h, "big")
return g1_scalar_base_mult(i)
```

```python
Expand Down Expand Up @@ -811,50 +818,56 @@ class SlotDecryptionIdentities(Container):
keyper_index: uint64
slot: uint64
txPointer: uint64
identities: List[Bytes64, ceil(ENCRYPTED_GAS_LIMIT / 21000)]
identity_preimages: List[Bytes52, ceil(ENCRYPTED_GAS_LIMIT / 21000)]

def generate_hash(
instance_id: uint64,
eon: uint64,
slot: uint64,
identities: Sequence[G1],
tx_pointer: uint64,
identity_preimages: Sequence[bytes],
) -> Bytes32:
sdi = SlotDecryptionIdentities(
instance_id=instance_id,
eon=eon,
slot=slot,
identities=[encode_g1(identity) for identity in identities],
txPointer=tx_pointer,
identity_preimages=identity_preimages,
)
return ssz.hash_tree_root(sdi)

def compute_slot_decryption_identities_signature(
instance_id: uint64,
eon: uint64,
slot: uint64,
identities: Sequence[G1],
tx_pointer: uint64,
identity_preimages: Sequence[bytes],
keyper_private_key: ECDSAPrivkey,
) -> ECDSASignature:
h = generate_hash(
instance_id,
eon,
slot,
identities,
tx_pointer,
identity_preimages,
)
return ecdsa.sign(keyper_private_key, h)

def check_slot_decryption_identities_signature(
instance_id: uint64,
eon: uint64,
slot: uint64,
identities: Sequence[G1],
txPointer: uint64,
identity_preimages: Sequence[bytes],
signature: ECDSASignature,
keyper_address: Address,
) -> bool:
h = generate_hash(
instance_id,
eon,
slot,
identities,
tx_pointer,
identity_preimages,
)
expected_pubkey = ecdsa.recover(h, signature)
return keyper_address == eth.pubkey_to_address(expected_pubkey)
Expand Down
Loading