def sign_block(self, signable_block: "model.BlockModel") -> str: """Sign a block with this class' keys Args: block: BlockModel to sign Returns: Base64 encoded string of the block signature Raises: exceptions.InvalidNodeLevel when invalid level on self RuntimeError when no private key is set on self """ if self.priv is None: raise RuntimeError("No private key has been set for signing") if self.level == 1: return crypto.sign_l1_block(self.hash, self.encryption, self.priv, signable_block) elif self.level == 2: return crypto.sign_l2_block(self.hash, self.encryption, self.priv, signable_block) elif self.level == 3: return crypto.sign_l3_block(self.hash, self.encryption, self.priv, signable_block) elif self.level == 4: return crypto.sign_l4_block(self.hash, self.encryption, self.priv, signable_block) elif self.level == 5: return crypto.sign_l5_block(self.hash, self.encryption, self.priv, signable_block) else: raise exceptions.InvalidNodeLevel( f"Node level {self.level} not implemented yet")
def get_broadcast_dto(higher_level: int, block_id: str) -> Dict[str, Any]: """Get the broadcast dto for a block to a certain level Args: higher_level: (2-5) block_id: block_id used to locate block in storage Returns: Broadcast DTO of requested """ l1_block = storage.get_json_from_object(f"{FOLDER}/{block_id}") if higher_level == 2: return l1_block_model.export_broadcast_dto(l1_block) else: required_verification_count = dragonnet_config.DRAGONNET_CONFIG[ f"l{higher_level - 1}"]["nodesRequired"] verification_blocks = get_verifications_for_l1_block( block_id, (higher_level - 1)) if len(verification_blocks) < required_verification_count: raise exceptions.NotEnoughVerifications( f"This chain requires {required_verification_count} level {higher_level - 1} verifications, but only {len(verification_blocks)} were found. (Probably just need to wait)" # noqa: B950 ) if higher_level == 3: return l2_block_model.export_broadcast_dto(verification_blocks, l1_block) elif higher_level == 4: return l3_block_model.export_broadcast_dto(verification_blocks, l1_block) elif higher_level == 5: return l4_block_model.export_broadcast_dto(verification_blocks) else: raise exceptions.InvalidNodeLevel( f"Level {higher_level} is not valid for getting a broadcast DTO (Only allowed 2-5)" )
def process_receipt_v1(block_dto: Dict[str, Any]) -> None: if not block_dto: raise exceptions.ValidationException("block_dto missing") _log.info( f"[RECEIPT] Got receipt from L{block_dto['header']['level']}: {block_dto}" ) block_model = cast("model.BlockModel", None) # This will always get defined, or it will raise level_received_from: int = block_dto["header"]["level"] if level_received_from == 2: block_model = l2_block_model.new_from_at_rest(block_dto) elif level_received_from == 3: block_model = l3_block_model.new_from_at_rest(block_dto) elif level_received_from == 4: block_model = l4_block_model.new_from_at_rest(block_dto) elif level_received_from == 5: block_model = l5_block_model.new_from_at_rest(block_dto) else: raise exceptions.InvalidNodeLevel("Unsupported level receipt") _log.info(f"Block model {block_model.__dict__}") l1_block_id_set = block_model.get_associated_l1_block_id() _log.info( f"Processing receipt for blocks {l1_block_id_set} from L{level_received_from}" ) for l1_block_id in l1_block_id_set: # Check that the chain which sent this receipt is in our claims, and that this L1 block is accepting receipts for this level validations = matchmaking.get_claim_check( l1_block_id)["validations"][f"l{level_received_from}"] if ( block_model.dc_id in validations ) and broadcast_functions.is_block_accepting_verifications_from_level( l1_block_id, level_received_from): _log.info( f"Verified that block {l1_block_id} was sent. Inserting receipt" ) storage_location = broadcast_functions.verification_storage_location( l1_block_id, level_received_from, block_model.dc_id) storage.put_object_as_json(storage_location, block_model.export_as_at_rest()) # Set new receipt for matchmaking claim check try: block_id = block_model.block_id proof = block_model.proof dc_id = block_model.dc_id matchmaking.add_receipt(l1_block_id, level_received_from, dc_id, block_id, proof) except Exception: _log.exception("matchmaking add_receipt failed!") # Update the broadcast system about this receipt broadcast_functions.set_receieved_verification_for_block_from_chain_sync( l1_block_id, level_received_from, block_model.dc_id) else: _log.warning( f"Chain {block_model.dc_id} (level {level_received_from}) returned a receipt that wasn't expected (possibly expired?) for block {l1_block_id}. Rejecting receipt" # noqa: B950 ) raise exceptions.NotAcceptingVerifications( f"Not accepting verifications for block {l1_block_id} from {block_model.dc_id}" )
def _get_verification_records( block_id: str, level: int = 0) -> Union[List[Any], Dict[str, List[Any]]]: if level: if level in [2, 3, 4, 5]: return _level_records(block_id, level) raise exceptions.InvalidNodeLevel(f"Level {level} not valid.") else: return _all_records(block_id)
def verify_full_transaction( self, full_tx: "transaction_model.TransactionModel") -> bool: """Verify a full transaction Args: full_tx: TransactionModel to verify Returns: Boolean if valid signed and hashed transaction Raises: exceptions.InvalidNodeLevel when self is not level 1 RuntimeError when no public key is set on self """ if self.level != 1: raise exceptions.InvalidNodeLevel( "Transactions only exist on level 1 nodes") if self.pub is None: raise RuntimeError("No public key has been set for verifying") return crypto.verify_full_transaction(self.hash, self.encryption, self.pub, full_tx)
def sign_transaction( self, signable_tx: "transaction_model.TransactionModel" ) -> Tuple[str, str]: """Sign a transaction Args: signable_tx: TransactionModel to sign Returns: Tuple of strings where index 0 is the base64 encoded full hash and index 1 is the base64 encoded stripped signature Raises: exceptions.InvalidNodeLevel when self is not level 1 RuntimeError when no private key is set on self """ if self.level != 1: raise exceptions.InvalidNodeLevel( "Transactions only exist on level 1 nodes") if self.priv is None: raise RuntimeError("No private key has been set for signing") return crypto.sign_transaction(self.hash, self.encryption, self.priv, signable_tx)
def setup() -> Tuple[Dict[str, str], Any]: cron_trigger = {"second": "*/1"} if LEVEL == "1": cron_trigger = {"second": "*/5"} from dragonchain.transaction_processor import level_1_actions as processor elif LEVEL == "2": from dragonchain.transaction_processor import level_2_actions as processor elif LEVEL == "3": from dragonchain.transaction_processor import level_3_actions as processor elif LEVEL == "4": from dragonchain.transaction_processor import level_4_actions as processor elif LEVEL == "5": cron_trigger = {"minute": "*/1"} from dragonchain.transaction_processor import level_5_actions as processor processor.setup() # L5 processor requires setup to be called to configure module state before running else: raise exceptions.InvalidNodeLevel("Invalid node level") return cron_trigger, processor
def pow_block(self, signable_block: "model.BlockModel") -> Tuple[str, int]: """Do proof of work on a block Args: block: BlockModel to do proof of work on Returns: Tuple where index 0 is a Base64 encoded string of the generated hash and index 1 is the nonce Raises: exceptions.InvalidNodeLevel when invalid level on self """ if self.level == 1: return crypto.pow_l1_block( self.hash, cast("l1_block_model.L1BlockModel", signable_block)) elif self.level == 2: return crypto.pow_l2_block( self.hash, cast("l2_block_model.L2BlockModel", signable_block)) elif self.level == 3: return crypto.pow_l3_block( self.hash, cast("l3_block_model.L3BlockModel", signable_block)) elif self.level == 4: return crypto.pow_l4_block( self.hash, cast("l4_block_model.L4BlockModel", signable_block)) else: raise exceptions.InvalidNodeLevel( f"Node level {self.level} not implemented yet")
def verify_block(self, block: "model.BlockModel") -> bool: # noqa: C901 """Verify a block with this class' keys Args: block: BlockModel to verify Returns: Boolean if valid block (according to these keys) Raises: NotImplementedError when invalid scheme on self """ if self.scheme == SupportedSchemes.trust and self.pub is None: RuntimeError( "No public key has been set for verifying block signature") if self.level == 1: if self.scheme == SupportedSchemes.trust: return crypto.verify_l1_block_trust( self.hash, self.encryption, self.pub, cast("l1_block_model.L1BlockModel", block)) elif self.scheme == SupportedSchemes.work: return crypto.verify_l1_block_pow( self.hash, cast("l1_block_model.L1BlockModel", block)) else: raise NotImplementedError( f"Proof scheme {self.scheme} not implemented") elif self.level == 2: if self.scheme == SupportedSchemes.trust: return crypto.verify_l2_block_trust( self.hash, self.encryption, self.pub, cast("l2_block_model.L2BlockModel", block)) elif self.scheme == SupportedSchemes.work: return crypto.verify_l2_block_pow( self.hash, cast("l2_block_model.L2BlockModel", block)) else: raise NotImplementedError( f"Proof scheme {self.scheme} not implemented") elif self.level == 3: if self.scheme == SupportedSchemes.trust: return crypto.verify_l3_block_trust( self.hash, self.encryption, self.pub, cast("l3_block_model.L3BlockModel", block)) elif self.scheme == SupportedSchemes.work: return crypto.verify_l3_block_pow( self.hash, cast("l3_block_model.L3BlockModel", block)) else: raise NotImplementedError( f"Proof scheme {self.scheme} not implemented") elif self.level == 4: if self.scheme == SupportedSchemes.trust: return crypto.verify_l4_block_trust( self.hash, self.encryption, self.pub, cast("l4_block_model.L4BlockModel", block)) elif self.scheme == SupportedSchemes.work: return crypto.verify_l4_block_pow( self.hash, cast("l4_block_model.L4BlockModel", block)) else: raise NotImplementedError( f"Proof scheme {self.scheme} not implemented") elif self.level == 5: if self.scheme == SupportedSchemes.trust: return crypto.verify_l5_block_trust( self.hash, self.encryption, self.pub, cast("l5_block_model.L5BlockModel", block)) else: raise NotImplementedError( f"Proof scheme {self.scheme} not implemented") else: raise exceptions.InvalidNodeLevel( f"Node level {self.level} nonexistent")
# with the following modification; you may not use this file except in # compliance with the Apache License and the following modification to it: # Section 6. Trademarks. is deleted and replaced with: # 6. Trademarks. This License does not grant permission to use the trade # names, trademarks, service marks, or product names of the Licensor # and its affiliates, except as required to comply with Section 4(c) of # the License and to reproduce the content of the NOTICE file. # You may obtain a copy of the Apache License at # http://www.apache.org/licenses/LICENSE-2.0 # Unless required by applicable law or agreed to in writing, software # distributed under the Apache License with the above modification is # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the Apache License for the specific # language governing permissions and limitations under the Apache License. import os import flask from dragonchain import exceptions from dragonchain.webserver.routes import route LEVEL = os.environ["LEVEL"] if LEVEL not in ["1", "2", "3", "4", "5"]: raise exceptions.InvalidNodeLevel(f"Invalid level {LEVEL}") app = flask.Flask(__name__) route(app)
def test_webserver_error_handler_invalid_node_level(self, mock_http_response, mock_report_exception): exception = exceptions.InvalidNodeLevel() helpers.webserver_error_handler(exception) mock_report_exception.assert_not_called() mock_http_response.assert_called_once_with(400, ANY)