mirror of https://github.com/nucypher/nucypher.git
Define initial schema for /retrieve_cfrags Porter endpoint - (untested at the moment)
parent
2cd7698da7
commit
61c0978e52
|
@ -164,7 +164,7 @@ class BobInterface(CharacterPublicInterface):
|
|||
policy_encrypting_key: PublicKey,
|
||||
alice_verifying_key: PublicKey,
|
||||
message_kit: bytes,
|
||||
encrypted_treasure_map: 'EncryptedTreasureMap'):
|
||||
encrypted_treasure_map: 'EncryptedTreasureMap') -> dict:
|
||||
"""
|
||||
Character control endpoint for re-encrypting and decrypting policy data.
|
||||
"""
|
||||
|
@ -180,7 +180,7 @@ class BobInterface(CharacterPublicInterface):
|
|||
return response_data
|
||||
|
||||
@attach_schema(bob.PublicKeys)
|
||||
def public_keys(self):
|
||||
def public_keys(self) -> dict:
|
||||
"""
|
||||
Character control endpoint for getting Bob's encrypting and signing public keys
|
||||
"""
|
||||
|
@ -193,7 +193,7 @@ class BobInterface(CharacterPublicInterface):
|
|||
class EnricoInterface(CharacterPublicInterface):
|
||||
|
||||
@attach_schema(enrico.EncryptMessage)
|
||||
def encrypt_message(self, plaintext: Union[str, bytes]):
|
||||
def encrypt_message(self, plaintext: Union[str, bytes]) -> dict:
|
||||
"""
|
||||
Character control endpoint for encrypting data for a policy and
|
||||
receiving the messagekit (and signature) to give to Bob.
|
||||
|
|
|
@ -19,6 +19,8 @@ from typing import List, Optional
|
|||
from eth_typing import ChecksumAddress
|
||||
|
||||
from nucypher.control.interfaces import ControlInterface, attach_schema
|
||||
from nucypher.crypto.umbral_adapter import PublicKey
|
||||
from nucypher.policy.kits import RetrievalKit
|
||||
from nucypher.utilities.porter.control.specifications import porter_schema
|
||||
|
||||
|
||||
|
@ -40,9 +42,7 @@ class PorterInterface(ControlInterface):
|
|||
exclude_ursulas=exclude_ursulas,
|
||||
include_ursulas=include_ursulas)
|
||||
|
||||
response_data = {
|
||||
"ursulas": ursulas_info
|
||||
}
|
||||
response_data = {"ursulas": ursulas_info}
|
||||
return response_data
|
||||
|
||||
@attach_schema(porter_schema.AliceRevoke)
|
||||
|
@ -52,3 +52,24 @@ class PorterInterface(ControlInterface):
|
|||
# 2. call self.implementer.some_function() i.e. Porter learner has an associated function to call
|
||||
# 3. create response
|
||||
pass
|
||||
|
||||
@attach_schema(porter_schema.BobRetrieveCFrags)
|
||||
def retrieve_cfrags(self,
|
||||
treasure_map: 'TreasureMap',
|
||||
retrieval_kits: List[RetrievalKit],
|
||||
alice_verifying_key: PublicKey,
|
||||
bob_encrypting_key: PublicKey,
|
||||
bob_verifying_key: PublicKey,
|
||||
policy_encrypting_key: PublicKey,
|
||||
publisher_verifying_key: Optional[PublicKey] = None,
|
||||
) -> dict:
|
||||
retrieval_results = self.implementer.retrieve_cfrags(treasure_map=treasure_map,
|
||||
retrieval_kits=retrieval_kits,
|
||||
alice_verifying_key=alice_verifying_key,
|
||||
bob_encrypting_key=bob_encrypting_key,
|
||||
bob_verifying_key=bob_verifying_key,
|
||||
policy_encrypting_key=policy_encrypting_key,
|
||||
publisher_verifying_key=publisher_verifying_key)
|
||||
results = retrieval_results.results # list of RetrievalResult objects
|
||||
response_data = {'retrieve_results': results}
|
||||
return response_data
|
||||
|
|
|
@ -17,3 +17,4 @@
|
|||
|
||||
from nucypher.utilities.porter.control.specifications.fields.ursula import *
|
||||
from nucypher.utilities.porter.control.specifications.fields.hrac import *
|
||||
from nucypher.utilities.porter.control.specifications.fields.retrieve import *
|
||||
|
|
|
@ -14,10 +14,13 @@
|
|||
You should have received a copy of the GNU Affero General Public License
|
||||
along with nucypher. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
from marshmallow import fields
|
||||
from nucypher.control.specifications.base import BaseSchema
|
||||
from nucypher.control.specifications.exceptions import InvalidInputData
|
||||
from nucypher.control.specifications.fields import Base64BytesRepresentation
|
||||
from nucypher.crypto.kits import RetrievalKit as RetrievalKitClass
|
||||
from nucypher.crypto.umbral_adapter import Capsule as CapsuleClass, CapsuleFrag as CapsuleFragClass
|
||||
from nucypher.utilities.porter.control.specifications.fields import UrsulaChecksumAddress
|
||||
|
||||
|
||||
# TODO should this be moved to character control - would this be used by a Bob character control endpoint?
|
||||
|
@ -29,3 +32,27 @@ class RetrievalKit(Base64BytesRepresentation):
|
|||
return RetrievalKitClass.from_bytes(retrieval_kit_bytes)
|
||||
except Exception as e:
|
||||
raise InvalidInputData(f"Could not convert input for {self.name} to a valid checksum address: {e}")
|
||||
|
||||
|
||||
class Capsule(Base64BytesRepresentation):
|
||||
def _deserialize(self, value, attr, data, **kwargs):
|
||||
try:
|
||||
capsule_bytes = super()._deserialize(value, attr, data, **kwargs)
|
||||
return CapsuleClass.from_bytes(capsule_bytes)
|
||||
except Exception as e:
|
||||
raise InvalidInputData(f"Could not parse {self.name}: {e}")
|
||||
|
||||
|
||||
class CapsuleFrag(Base64BytesRepresentation):
|
||||
def _deserialize(self, value, attr, data, **kwargs):
|
||||
try:
|
||||
capsule_bytes = super()._deserialize(value, attr, data, **kwargs)
|
||||
return CapsuleFragClass.from_bytes(capsule_bytes)
|
||||
except Exception as e:
|
||||
raise InvalidInputData(f"Could not parse {self.name}: {e}")
|
||||
|
||||
|
||||
class RetrievalResultSchema(BaseSchema):
|
||||
"""Schema for the result of retrieve_cfrags."""
|
||||
capsule = Capsule()
|
||||
cfrags = fields.Dict(keys=UrsulaChecksumAddress(), values=CapsuleFrag())
|
||||
|
|
|
@ -117,3 +117,74 @@ class AliceGetUrsulas(BaseSchema):
|
|||
|
||||
class AliceRevoke(BaseSchema):
|
||||
pass # TODO need to understand revoke process better
|
||||
|
||||
|
||||
#
|
||||
# Bob Endpoints
|
||||
#
|
||||
class BobRetrieveCFrags(BaseSchema):
|
||||
treasure_map = character_fields.TreasureMap(
|
||||
required=True,
|
||||
load_only=True,
|
||||
click=click.option(
|
||||
'--treasure-map',
|
||||
'-t',
|
||||
help="Treasure Map to publish",
|
||||
type=click.STRING,
|
||||
required=True))
|
||||
retrieval_kits = base_fields.List(
|
||||
fields.RetrievalKit(),
|
||||
click=click.option(
|
||||
'--retrieval-kits',
|
||||
'-r',
|
||||
help="Retrieval kits for reencryption",
|
||||
multiple=True,
|
||||
type=click.STRING,
|
||||
required=True,
|
||||
default=[]),
|
||||
required=True,
|
||||
load_only=True)
|
||||
alice_verifying_key = character_fields.Key(
|
||||
required=True,
|
||||
load_only=True,
|
||||
click=click.option(
|
||||
'--alice-verifying-key',
|
||||
'-avk',
|
||||
help="Alice's verifying key as a hexadecimal string",
|
||||
type=click.STRING,
|
||||
required=True))
|
||||
bob_encrypting_key = character_fields.Key(
|
||||
required=True,
|
||||
load_only=True,
|
||||
click=option_bob_encrypting_key())
|
||||
bob_verifying_key = character_fields.Key(
|
||||
required=True,
|
||||
load_only=True,
|
||||
click=click.option(
|
||||
'--bob-verifying-key',
|
||||
'-bvk',
|
||||
help="Bob's verifying key as a hexadecimal string",
|
||||
type=click.STRING,
|
||||
required=True))
|
||||
policy_encrypting_key = character_fields.Key(
|
||||
required=True,
|
||||
load_only=True,
|
||||
click=click.option(
|
||||
'--policy-encrypting-key',
|
||||
help="Encrypting Public Key for Policy as hexadecimal string",
|
||||
type=click.STRING,
|
||||
required=True))
|
||||
|
||||
# optional
|
||||
publisher_verifying_key = character_fields.Key(
|
||||
required=False,
|
||||
load_only=True,
|
||||
click=click.option(
|
||||
'--publisher-verifying-key',
|
||||
'-pvk',
|
||||
help="Alice's verifying key as a hexadecimal string",
|
||||
type=click.STRING,
|
||||
required=False))
|
||||
|
||||
# output
|
||||
retrieval_results = marshmallow_fields.List(marshmallow_fields.Nested(fields.RetrievalResultSchema), dump_only=True)
|
||||
|
|
|
@ -23,11 +23,14 @@ from flask import request, Response
|
|||
from nucypher.blockchain.eth.agents import ContractAgency, StakingEscrowAgent
|
||||
from nucypher.blockchain.eth.interfaces import BlockchainInterfaceFactory
|
||||
from nucypher.blockchain.eth.registry import BaseContractRegistry, InMemoryContractRegistry
|
||||
from nucypher.characters.lawful import Ursula
|
||||
from nucypher.characters.lawful import RetrievalClient, Ursula
|
||||
from nucypher.control.controllers import JSONRPCController, WebController
|
||||
from nucypher.crypto.powers import DecryptingPower
|
||||
from nucypher.crypto.umbral_adapter import PublicKey
|
||||
from nucypher.network.nodes import Learner
|
||||
from nucypher.policy.kits import RetrievalKit
|
||||
from nucypher.policy.maps import TreasureMap
|
||||
from nucypher.policy.orders import RetrievalResult
|
||||
from nucypher.policy.reservoir import (
|
||||
make_federated_staker_reservoir,
|
||||
make_decentralized_staker_reservoir,
|
||||
|
@ -148,14 +151,18 @@ the Pipe for nucypher network operations
|
|||
ursulas_info = successes.values()
|
||||
return list(ursulas_info)
|
||||
|
||||
def exec_work_order(self, ursula_address: ChecksumAddress, work_order_payload: bytes) -> bytes:
|
||||
self.block_until_specific_nodes_are_known(addresses={ursula_address}, learn_on_this_thread=True)
|
||||
ursula = self.known_nodes[ursula_address]
|
||||
ursula_rest_response = self.network_middleware.send_work_order_payload_to_ursula(
|
||||
ursula=ursula,
|
||||
work_order_payload=work_order_payload)
|
||||
result = ursula_rest_response.content
|
||||
return result
|
||||
def retrieve_cfrags(self,
|
||||
treasure_map: TreasureMap,
|
||||
retrieval_kits: Sequence[RetrievalKit],
|
||||
alice_verifying_key: PublicKey,
|
||||
bob_encrypting_key: PublicKey,
|
||||
bob_verifying_key: PublicKey,
|
||||
policy_encrypting_key: PublicKey,
|
||||
publisher_verifying_key: Optional[PublicKey] = None) -> List[RetrievalResult]:
|
||||
client = RetrievalClient(self)
|
||||
return client.retrieve_cfrags(treasure_map, retrieval_kits,
|
||||
alice_verifying_key, bob_encrypting_key, bob_verifying_key, policy_encrypting_key,
|
||||
publisher_verifying_key)
|
||||
|
||||
def _make_staker_reservoir(self,
|
||||
quantity: int,
|
||||
|
@ -229,10 +236,10 @@ the Pipe for nucypher network operations
|
|||
response = controller(method_name='revoke', control_request=request)
|
||||
return response
|
||||
|
||||
@porter_flask_control.route("/exec_work_order", methods=['POST'])
|
||||
def exec_work_order() -> Response:
|
||||
@porter_flask_control.route("/retrieve_cfrags", methods=['POST'])
|
||||
def retrieve_cfrags() -> Response:
|
||||
"""Porter control endpoint for executing a PRE work order on behalf of Bob."""
|
||||
response = controller(method_name='exec_work_order', control_request=request)
|
||||
response = controller(method_name='retrieve_cfrags', control_request=request)
|
||||
return response
|
||||
|
||||
return controller
|
||||
|
|
|
@ -21,12 +21,12 @@ import pytest
|
|||
from nucypher.control.specifications.exceptions import InvalidInputData
|
||||
from nucypher.control.specifications.fields import StringList
|
||||
from nucypher.crypto.kits import RetrievalKit as RetrievalKitClass
|
||||
from nucypher.crypto.umbral_adapter import SecretKey, encrypt
|
||||
from nucypher.crypto.umbral_adapter import SecretKey, encrypt, Capsule as CapsuleClass
|
||||
from nucypher.utilities.porter.control.specifications.fields import (
|
||||
HRAC,
|
||||
UrsulaChecksumAddress,
|
||||
)
|
||||
from nucypher.utilities.porter.control.specifications.fields.retrieve import RetrievalKit
|
||||
from nucypher.utilities.porter.control.specifications.fields.retrieve import RetrievalKit, Capsule
|
||||
from tests.utils.policy import retrieval_request_setup
|
||||
|
||||
|
||||
|
@ -156,3 +156,19 @@ def test_retrieval_kit_field():
|
|||
|
||||
with pytest.raises(InvalidInputData):
|
||||
field.deserialize(value=b64encode(b"invalid_retrieval_kit_bytes").decode(), attr=None, data=None)
|
||||
|
||||
|
||||
def test_capsule_field():
|
||||
encrypting_key = SecretKey.random().public_key()
|
||||
capsule, _ = encrypt(encrypting_key, b'testing_retrieval_kit')
|
||||
|
||||
field = Capsule()
|
||||
serialized = field._serialize(value=capsule, attr=None, obj=None)
|
||||
assert serialized == b64encode(bytes(capsule)).decode()
|
||||
|
||||
deserialized = field._deserialize(value=serialized, attr=None, data=None)
|
||||
assert isinstance(deserialized, CapsuleClass)
|
||||
assert deserialized == capsule
|
||||
|
||||
with pytest.raises(InvalidInputData):
|
||||
field._deserialize(value=b64encode(b"faux_capsule").decode(), attr=None, data=None)
|
||||
|
|
Loading…
Reference in New Issue