def flatten_delta_crdt(self, receiver_id, sender_ids, sequence, wait=None): addresses = [make_entity_address(receiver_id)] for sender in sender_ids: addresses.append(make_entity_address(sender)) message = {'entity_id': receiver_id, 'sqn': sequence} return self._send_transaction(ActionTypes.FLATTEN_DELTA_CRDT.value, cbor2.dumps(message), addresses, wait=wait)
def add_crdt_record(self, receive_entity, send_entity, crdt_record, wait=None): crdt_address = make_crdt_address(receive_entity) receiver_address = make_entity_address(receive_entity) sender_address = make_entity_address(send_entity) return self._send_transaction(ActionTypes.ADD_LEDGER_CRDT.value, crdt_record, [ crdt_address, receiver_address, sender_address, ], wait=wait)
def _add_user(serialized_payload, context): home_id, blob_sig, user_id, user_blob = _parse_add_user(serialized_payload) # TODO(matt9j) Validate the network signature against the known home network key! address = make_entity_address(user_id) data = _get_state_data(address, context) if data: raise InvalidTransaction('The user {} already exists'.format(user_id)) _set_state_data(address, user_blob, context)
def _add_net(action_payload, context): # TODO(matt9j) Do a more safe deserialization. # Parse the outer data layer from wire format anchor_id, message_sig, community_id, new_community_blob = \ _parse_add_community(action_payload) # TODO(matt9j) Validate the anchor and signature verified = True # key = nacl.signing.VerifyKey(message['key'], # encoder=nacl.encoding.RawEncoder) # verified = key.verify(raw_message, signature, # encoder=nacl.encoding.RawEncoder) if not verified: raise InvalidTransaction('Message failed network key validation') address = make_entity_address(community_id) data = _get_state_data(address, context) if data: raise InvalidTransaction( 'The community {} already exists'.format(community_id)) _set_state_data(address, new_community_blob, context)
def _add_ledger_crdt(action_payload, context): """Add a crdt record to the on-chain crdt implementation.""" exchange = asterales_parsers.parse_exchange_record(action_payload) # TODO(matt9j) handle gaps in the receive SQN? # TODO(matt9j) Validate that the sequence number has indeed progressed receive_sqn = exchange.receiver_sequence_number_msb * (2 ** 64) + \ exchange.receiver_sequence_number_lsb LOG.debug("Processing id: %d, sqn: %d to the ledger crdt", exchange.receiver_id, receive_sqn) # Validate the exchange signatures receiver_blob = _get_state_data(make_entity_address(exchange.receiver_id), context) receiver_data = storage_pb2.Entity() receiver_data.ParseFromString(receiver_blob) receive_verify_key = nacl.signing.VerifyKey( receiver_data.verify_key, encoder=nacl.encoding.RawEncoder) try: receive_verify_key.verify(exchange.receiver_signed_blob, exchange.receiver_signature, encoder=nacl.encoding.RawEncoder) except nacl.exceptions.BadSignatureError as e: LOG.error(e) raise InvalidTransaction( 'Exchange receive signature invalid sqn:{}'.format(receive_sqn)) sender_blob = _get_state_data(make_entity_address(exchange.sender_id), context) sender_data = storage_pb2.Entity() sender_data.ParseFromString(sender_blob) sender_verify_key = nacl.signing.VerifyKey( sender_data.verify_key, encoder=nacl.encoding.RawEncoder) try: sender_verify_key.verify(exchange.sender_signed_blob, exchange.sender_signature, encoder=nacl.encoding.RawEncoder) except nacl.exceptions.BadSignatureError: raise InvalidTransaction( 'Exchange send signature invalid sqn:{}'.format(receive_sqn)) crdt_address = make_crdt_address(exchange.receiver_id) current_crdt_blob = _get_state_data(crdt_address, context) if current_crdt_blob is not None: crdt_history = cbor.loads(current_crdt_blob) else: crdt_history = [] if receive_sqn in crdt_history: LOG.info("Discarding duplicate upload sqn: %d", receive_sqn) return LOG.info("Record is new, adding id: %d, sqn: %d to the ledger crdt", exchange.receiver_id, receive_sqn) crdt_history.append(receive_sqn) _set_state_data(crdt_address, cbor.dumps(crdt_history), context) receiver_data.balance += exchange.amount _set_state_data(make_entity_address(exchange.receiver_id), receiver_data.SerializeToString(), context) sender_data.balance -= exchange.amount _set_state_data(make_entity_address(exchange.sender_id), sender_data.SerializeToString(), context)
def _flatten_delta_crdt(action_payload, context, crdt_endpoint): """Flattens contiguous local CRDT records into the entity state""" # Parse the request. flatten_request = cbor2.loads(action_payload) # TODO(matt9j) Use the origin as a fallback if deltas are missing. # request_origin = flatten_request['origin'] entity_id = flatten_request['entity_id'] proposed_frontier = flatten_request['sqn'] LOG.info("Attempting to flatten delta crdt for id: %d to frontier_sqn: %d", entity_id, proposed_frontier) # Lookup the current frontier sqn. entity_blob = _get_state_data(make_entity_address(entity_id), context) entity_pb = storage_pb2.Entity() entity_pb.ParseFromString(entity_blob) current_frontier = entity_pb.frontier_sequence_number LOG.debug('requesting endorsement for (%d, %d] for entity %d', current_frontier, proposed_frontier, entity_id) # ask the local CRDT for exchanges up to the new proposed frontier SQN crdt_request = { 'receive_id': entity_id, 'current_frontier': current_frontier, 'proposed_frontier': proposed_frontier, } response = CRDT_SESSION.post( url=crdt_endpoint + "/crdt/endorsingRecords", data=cbor2.dumps(crdt_request), headers={'Content-Type': 'application/octet-stream'}) if not response.ok: LOG.error("Unable to request endorsing crdt records %s", response) raise InvalidTransaction("Unable to request endorsement from CRDT") # Set aside the receiver information receiver_blob = _get_state_data(make_entity_address(entity_id), context) receiver_data = storage_pb2.Entity() receiver_data.ParseFromString(receiver_blob) receive_verify_key = nacl.signing.VerifyKey( receiver_data.verify_key, encoder=nacl.encoding.RawEncoder) # exchanges must be sorted already in ascending order by sequence number exchanges = cbor2.loads(response.content) frontier = current_frontier # TODO(matt9j) Possibly speedup by re-using exchange protobufs # validate all the exchanges for exchange_blob in exchanges: if frontier >= proposed_frontier: LOG.warning("Got too many exchange records or bad frontier req?!") break exchange = asterales_parsers.parse_exchange_record(exchange_blob) receive_sqn = exchange.receiver_sequence_number_msb * (2 ** 64) + \ exchange.receiver_sequence_number_lsb last_valid_receive_sqn = exchange.last_valid_sequence_number_msb * (2**64) + \ exchange.last_valid_sequence_number_lsb # Validate signatures try: receive_verify_key.verify(exchange.receiver_signed_blob, exchange.receiver_signature, encoder=nacl.encoding.RawEncoder) except nacl.exceptions.BadSignatureError as e: LOG.error(e) raise InvalidTransaction( 'Exchange receive signature invalid sqn:{}'.format( receive_sqn)) sender_blob = _get_state_data(make_entity_address(exchange.sender_id), context) sender_data = storage_pb2.Entity() sender_data.ParseFromString(sender_blob) sender_verify_key = nacl.signing.VerifyKey( sender_data.verify_key, encoder=nacl.encoding.RawEncoder) try: sender_verify_key.verify(exchange.sender_signed_blob, exchange.sender_signature, encoder=nacl.encoding.RawEncoder) except nacl.exceptions.BadSignatureError: raise InvalidTransaction( 'Exchange send signature invalid sqn:{}'.format(receive_sqn)) if last_valid_receive_sqn != frontier: LOG.warning("Gap discovered-- in this hacky initial implementation" " state may now be corrupted.") raise InvalidTransaction() receiver_data.balance += exchange.amount # TODO(matt9j) Don't serialize all the send messages until done! Some # may be repeated... sender_data.balance -= exchange.amount _set_state_data(make_entity_address(exchange.sender_id), sender_data.SerializeToString(), context) frontier = receive_sqn # Serialize the common receive buffer at the end of processing. # TODO(matt9j) Fix LSB/MSB precision laziness. frontier_sequence_number_lsb = frontier & 0xFFFFFFFFFFFFFFFF frontier_sequence_number_msb = frontier >> 64 receiver_data.frontier_sequence_number = frontier_sequence_number_lsb _set_state_data(make_entity_address(exchange.receiver_id), receiver_data.SerializeToString(), context)
def add_user(self, user_id, payload, wait=None): address = make_entity_address(user_id) return self._send_transaction(ActionTypes.ADD_USER.value, payload, [address], wait=wait)
def add_community(self, network_id, payload, wait=None): address = make_entity_address(network_id) return self._send_transaction(ActionTypes.ADD_NET.value, payload, [address], wait=wait)