Ursula confirms ritual participation before responding to a decryption request

pull/3115/head
Kieran Prasch 2023-04-12 16:46:13 -07:00
parent a1d6ae77f9
commit c7105b52db
5 changed files with 38 additions and 26 deletions

View File

@ -644,7 +644,13 @@ class CoordinatorAgent(EthereumContractAgent):
return receipt
@contract_api(TRANSACTION)
def post_transcript(self, ritual_id: int, transcript: bytes, node_index: int, transacting_power: TransactingPower) -> TxReceipt:
def post_transcript(
self,
ritual_id: int,
transcript: bytes,
node_index: int,
transacting_power: TransactingPower
) -> TxReceipt:
contract_function: ContractFunction = self.contract.functions.postTranscript(
ritualId=ritual_id,
nodeIndex=node_index,
@ -659,16 +665,17 @@ class CoordinatorAgent(EthereumContractAgent):
self,
ritual_id: int,
node_index: int,
aggregated_transcript: AggregatedTranscript,
aggregated_transcript: bytes,
transacting_power: TransactingPower,
) -> TxReceipt:
contract_function: ContractFunction = self.contract.functions.postAggregation(
ritualId=ritual_id,
nodeIndex=node_index,
aggregatedTranscript=bytes(aggregated_transcript),
aggregatedTranscript=aggregated_transcript,
)
receipt = self.blockchain.send_transaction(
contract_function=contract_function, transacting_power=transacting_power
contract_function=contract_function,
transacting_power=transacting_power
)
return receipt

View File

@ -1,17 +1,7 @@
import os
import time
from typing import Callable, List, Optional, Tuple, Type, Union
from eth_typing import ChecksumAddress
from ferveo_py import (
AggregatedTranscript,
DecryptionShare,
Dkg,
Keypair,
PublicKey,
Transcript,
)
from twisted.internet import threads
from typing import Callable, List, Optional, Tuple, Type, Union
from web3 import Web3
from web3.contract import Contract, ContractEvent
from web3.datastructures import AttributeDict
@ -60,7 +50,7 @@ class EventScannerTask(SimpleTask):
raise args[0]
class RitualTracker:
class ActiveRitualTracker:
MAX_CHUNK_SIZE = 10000
@ -68,8 +58,9 @@ class RitualTracker:
ritualist,
eth_provider: BaseProvider,
contract: Contract,
start_block: int = 0,
persistent: bool = False):
start_block: int = 0, # TODO: use a start block that correlates to the ritual timeout
persistent: bool = False # TODO: use persistent storage?
):
self.log = Logger("RitualTracker")

View File

@ -15,7 +15,8 @@ from cryptography.hazmat.primitives.serialization import Encoding
from cryptography.x509 import Certificate, NameOID
from eth_typing.evm import ChecksumAddress
from eth_utils import to_checksum_address
from ferveo_py import Ciphertext, DecryptionShare, combine_decryption_shares, decrypt_with_shared_secret
from ferveo_py import Ciphertext, DecryptionShare, combine_decryption_shares, decrypt_with_shared_secret, \
ExternalValidator, Transcript
from pathlib import Path
from queue import Queue
from twisted.internet import reactor
@ -38,7 +39,7 @@ import nucypher
from nucypher.acumen.nicknames import Nickname
from nucypher.acumen.perception import ArchivedFleetState, RemoteUrsulaStatus
from nucypher.blockchain.eth.actors import Operator, PolicyAuthor, Ritualist
from nucypher.blockchain.eth.agents import ContractAgency, PREApplicationAgent
from nucypher.blockchain.eth.agents import ContractAgency, PREApplicationAgent, CoordinatorAgent
from nucypher.blockchain.eth.interfaces import BlockchainInterfaceFactory
from nucypher.blockchain.eth.registry import BaseContractRegistry
from nucypher.blockchain.eth.signers.software import Web3Signer
@ -50,6 +51,7 @@ from nucypher.characters.banners import (
)
from nucypher.characters.base import Character, Learner
from nucypher.config.storages import NodeStorage
from nucypher.crypto.ferveo.dkg import aggregate_transcripts
from nucypher.crypto.keypairs import HostingKeypair
from nucypher.crypto.powers import (
DecryptingPower,
@ -1121,6 +1123,14 @@ class Ursula(Teacher, Character, Operator, Ritualist):
balance_eth=balance_eth,
)
def as_external_validator(self) -> ExternalValidator:
"""Returns an ExternalValidator instance for this Ursula for use in DKG operations."""
validator = ExternalValidator(
address=self.checksum_address,
public_key=self.public_keys(RitualisticPower)
)
return validator
class LocalUrsulaStatus(NamedTuple):
nickname: Nickname

View File

@ -1,5 +1,3 @@
import requests
import socket

View File

@ -143,18 +143,23 @@ def _make_rest_app(this_node, log: Logger) -> Flask:
decryption_request = ThresholdDecryptionRequest.from_bytes(request.data)
# Deserialize and instantiate ConditionLingo from the request data
lingo = ConditionLingo.from_list(json.loads(str(decryption_request.conditions)))
# requester-supplied condition eval context
# evaluate the conditions for this ciphertext
lingo = ConditionLingo.from_list(json.loads(str(decryption_request.conditions)))
context = None
if decryption_request.context:
context = json.loads(str(decryption_request.context)) or dict()
# evaluate the conditions for this ciphertext
error = evaluate_condition_lingo(lingo, context)
if error:
return Response(error.message, status=error.status_code)
# TODO: confirm this node is tracking the ritual and is an authorized recipient
# dkg_public_key = this_node.dkg_storage.get_public_key(decryption_request.ritual_id)
ritual = this_node.coordinator_agent.get_ritual(decryption_request.ritual_id, with_participants=True)
participants = [p.node for p in ritual.participants]
if this_node.checksum_address not in participants:
return Response(f'Node not part of ritual {decryption_request.ritual_id}', status=HTTPStatus.FORBIDDEN)
# derive the decryption share
decryption_share = this_node.derive_decryption_share(
ritual_id=decryption_request.ritual_id,
@ -163,6 +168,7 @@ def _make_rest_app(this_node, log: Logger) -> Flask:
)
# return the decryption share
# TODO: encrypt the response with the requester's public key # 3079
response = ThresholdDecryptionResponse(decryption_share=decryption_share)
return Response(bytes(response), headers={'Content-Type': 'application/octet-stream'})