def is_valid_batch(batch): # validate batch signature header = BatchHeader() header.ParseFromString(batch.header) context = create_context('secp256k1') public_key = Secp256k1PublicKey.from_hex(header.signer_public_key) if not context.verify(batch.header_signature, batch.header, public_key): LOGGER.debug("batch failed signature validation: %s", batch.header_signature) return False # validate all transactions in batch for txn in batch.transactions: if not is_valid_transaction(txn): return False txn_header = TransactionHeader() txn_header.ParseFromString(txn.header) if txn_header.batcher_public_key != header.signer_public_key: LOGGER.debug( "txn batcher public_key does not match signer" "public_key for batch: %s txn: %s", batch.header_signature, txn.header_signature) return False return True
def handle(self, connection_id, message_content): batch_header = BatchHeader() for batch in message_content.batches: batch_header.ParseFromString(batch.header) if batch_header.signer_public_key == self._whitelist_public_key: # There is a whitelisted batch, so allow it to continue return HandlerResult(status=HandlerStatus.PASS) batch_header.Clear() if self._is_batch_pool_full(): if not self._applying_backpressure: self._applying_backpressure = True LOGGER.info( 'Applying back pressure on client submitted batches') self._batches_rejected_count.inc() self._batches_rejected_gauge.set_value( self._batches_rejected_gauge.get_value() + 1) response = ClientBatchSubmitResponse( status=ClientBatchSubmitResponse.QUEUE_FULL) return HandlerResult( status=HandlerStatus.RETURN, message_out=response, message_type=Message.CLIENT_BATCH_SUBMIT_RESPONSE ) if self._applying_backpressure: self._applying_backpressure = False self._batches_rejected_gauge.set_value(0) LOGGER.info('Ending back pressure on client submitted batches') return HandlerResult(status=HandlerStatus.PASS)
def is_batch_signer_authorized(self, batch, state_root=None, from_state=False): """ Check the batch signing key against the allowed transactor permissions. The roles being checked are the following, from first to last: "transactor.batch_signer" "transactor" "default" The first role that is set will be the one used to enforce if the batch signer is allowed. Args: batch (Batch): The batch that is being verified. state_root(string): The state root of the previous block. If this is None, the current state root hash will be retrieved. from_state (bool): Whether the identity value should be read directly from state, instead of using the cached values. This should be used when the state_root passed is not from the current chain head. """ if state_root is None: state_root = self._current_root_func() LOGGER.debug("authorized Chain head is %s.", state_root) if state_root == INIT_ROOT_KEY: LOGGER.debug("Chain head is not set yet. Permit all.") return True self._cache.update_view(state_root) header = BatchHeader() header.ParseFromString(batch.header) role = self._cache.get_role("transactor.batch_signer", state_root, from_state) if role is None: role = self._cache.get_role("transactor", state_root, from_state) if role is None: policy_name = "default" else: policy_name = role.policy_name policy = self._cache.get_policy(policy_name, state_root, from_state) if policy is None: allowed = True else: allowed = self._allowed(header.signer_public_key, policy) if allowed: return self.is_transaction_signer_authorized( batch.transactions, state_root, from_state) LOGGER.debug("Batch Signer: %s is not permitted.", header.signer_public_key) return False
def is_valid_batch(batch): # batch structure verification header = BatchHeader() header.ParseFromString(batch.header) # check whether a batch has duplicate transactions if len(header.transaction_ids) != len(set(header.transaction_ids)): LOGGER.debug("Batch has duplicate transactions. Dropping batch: %s", batch.header_signature) return False # validate the transaction_ids field in batch header contains a list of # transaction header_signatures and must be the same order as the # transactions field if len(batch.transactions) > len(header.transaction_ids): LOGGER.debug("Batch has extra transactions. Dropping batch: %s", batch.header_signature) return False if len(batch.transactions) < len(header.transaction_ids): LOGGER.debug("Batch lacks transactions. Dropping batch: %s", batch.header_signature) return False for header_txn_id, txn in zip(header.transaction_ids, batch.transactions): if header_txn_id != txn.header_signature: LOGGER.debug( "The header.transaction_ids does not match the " "order of transactions in the batch: %s txn: %s", batch.header_signature, header_txn_id) return False return True
def _create_batches(self, batch_count, txn_count, valid_batch=True, valid_txn=True, valid_structure=True, valid_batcher=True): batch_list = [] for i in range(batch_count): txn_list = self._create_transactions(txn_count, valid_txn, valid_batcher) txn_sig_list = [txn.header_signature for txn in txn_list] if not valid_structure: txn_sig_list.pop() batch_header = BatchHeader(signer_pubkey=self.public_key) batch_header.transaction_ids.extend(txn_sig_list) header_bytes = batch_header.SerializeToString() if valid_batch: signature = signing.sign(header_bytes, self.private_key) else: signature = "bad_signature" batch = Batch(header=header_bytes, transactions=txn_list, header_signature=signature) batch_list.append(batch) return batch_list
def is_valid_batch(batch): # validate batch signature header = BatchHeader() header.ParseFromString(batch.header) if not signing.verify(batch.header, batch.header_signature, header.signer_public_key): LOGGER.debug("batch failed signature validation: %s", batch.header_signature) return False # validate all transactions in batch for txn in batch.transactions: if not is_valid_transaction(txn): return False txn_header = TransactionHeader() txn_header.ParseFromString(txn.header) if txn_header.batcher_public_key != header.signer_public_key: LOGGER.debug( "txn batcher public_key does not match signer" "public_key for batch: %s txn: %s", batch.header_signature, txn.header_signature) return False return True
def _generate_batch(self, payload): payload_encoded = payload.encode('utf-8') hasher = hashlib.sha512() hasher.update(payload_encoded) header = TransactionHeader() header.batcher_pubkey = self.public_key # txn.dependencies not yet header.family_name = 'test' header.family_version = '1' header.nonce = _generate_id(16) header.payload_encoding = "text" header.payload_sha512 = hasher.hexdigest().encode() header.signer_pubkey = self.public_key txn = Transaction() header_bytes = header.SerializeToString() txn.header = header_bytes txn.header_signature = signing.sign(header_bytes, self.signing_key) txn.payload = payload_encoded batch_header = BatchHeader() batch_header.signer_pubkey = self.public_key batch_header.transaction_ids.extend([txn.header_signature]) batch = Batch() header_bytes = batch_header.SerializeToString() batch.header = header_bytes batch.header_signature = signing.sign(header_bytes, self.signing_key) batch.transactions.extend([txn]) return batch
def validate_batch(batch): # validate batch signature header = BatchHeader() header.ParseFromString(batch.header) valid = signing.verify(batch.header, batch.header_signature, header.signer_pubkey) if not valid: LOGGER.debug("batch failed signature validation: %s", batch.header_signature) # validate all transactions in batch total = len(batch.transactions) index = 0 while valid and index < total: txn = batch.transactions[index] valid = validate_transaction(txn) if valid: txn_header = TransactionHeader() txn_header.ParseFromString(txn.header) if txn_header.batcher_pubkey != header.signer_pubkey: LOGGER.debug( "txn batcher pubkey does not match signer" "pubkey for batch: %s txn: %s", batch.header_signature, txn.header_signature) valid = False index += 1 return valid
def _create_batches(self, batch_count, txn_count, missing_dep=False): batch_list = [] for i in range(batch_count): txn_list = self._create_transactions(txn_count, missing_dep=missing_dep) txn_sig_list = [txn.header_signature for txn in txn_list] batch_header = BatchHeader(signer_pubkey=self.public_key) batch_header.transaction_ids.extend(txn_sig_list) header_bytes = batch_header.SerializeToString() signature = signing.sign( header_bytes, self.private_key) batch = Batch(header=header_bytes, transactions=txn_list, header_signature=signature) batch_list.append(batch) return batch_list
def is_batch_signer_authorized(batch, allowed_pubkeys): header = BatchHeader() header.ParseFromString(batch.header) if header.signer_pubkey not in allowed_pubkeys: LOGGER.info("Batch was signed by an unauthorized signing key %s", header.signer_pubkey) return False return True
def _make_mock_batch(self, batch_id='batch_id'): txn = _make_mock_transaction(batch_id) header = BatchHeader(signer_pubkey='pubkey', transaction_ids=[txn.header_signature]) return Batch(header=header.SerializeToString(), header_signature=batch_id, transactions=[txn])
def make_mock_batch(base_id='id'): batch_id = 'b-' + base_id txn = _make_mock_transaction(base_id) header = BatchHeader(signer_public_key='public_key-' + base_id, transaction_ids=[txn.header_signature]) return Batch(header=header.SerializeToString(), header_signature=batch_id, transactions=[txn])
def make_mock_batch(base_id='id'): batch_id = 'a' * (128 - len(base_id)) + base_id txn = _make_mock_transaction(base_id) signer_public_key = b'public_key' + bytes(base_id, 'utf-8') header = BatchHeader(signer_public_key=signer_public_key.hex(), transaction_ids=[txn.header_signature]) return Batch(header=header.SerializeToString(), header_signature=batch_id, transactions=[txn])
def is_batch_signer_authorized(self, batch, state_root=None): """ Check the batch signing key against the allowed transactor permissions. The roles being checked are the following, from first to last: "transactor.batch_signer" "transactor" "default" The first role that is set will be the one used to enforce if the batch signer is allowed. Args: batch (Batch): The batch that is being verified. state_root(string): The state root of the previous block. If this is None, the current state root hash will be retrieved. """ if state_root is None: state_root = self._current_root_func() if state_root == INIT_ROOT_KEY: LOGGER.debug("Chain head is not set yet. Permit all.") return True identity_view = \ self._identity_view_factory.create_identity_view(state_root) header = BatchHeader() header.ParseFromString(batch.header) role = \ identity_view.get_role("transactor.batch_signer") if role is None: role = identity_view.get_role("transactor") if role is None: policy_name = "default" else: policy_name = role.policy_name policy = identity_view.get_policy(policy_name) if policy is None: allowed = True else: allowed = self._allowed(header.signer_pubkey, policy) if allowed: return self.is_transaction_signer_authorized( batch.transactions, identity_view) LOGGER.debug("Batch Signer: %s is not permitted.", header.signer_pubkey) return False
def create_batch(transactions, signer): transaction_signatures = [t.header_signature for t in transactions] header = BatchHeader(signer_public_key=signer.get_public_key().as_hex(), transaction_ids=transaction_signatures) header_bytes = header.SerializeToString() signature = signer.sign(header_bytes) batch = Batch(header=header_bytes, transactions=transactions, header_signature=signature) return batch
def _generate_batch(self, txn_count=2, missing_deps=False, txns=None): if txns is None: txns = [] if txn_count != 0: txns += [ self.generate_transaction('txn_' + str(i)) for i in range(txn_count) ] if missing_deps: target_txn = txns[-1] txn_missing_deps = self.generate_transaction( payload='this one has a missing dependency', deps=[target_txn.header_signature]) # replace the targeted txn with the missing deps txn txns[-1] = txn_missing_deps batch_header = BatchHeader( signer_public_key=self.signer.get_public_key().as_hex(), transaction_ids=[txn.header_signature for txn in txns]).SerializeToString() batch = Batch(header=batch_header, header_signature=self.signer.sign(batch_header), transactions=txns) return batch
def create_batch(self, block_info): payload = BlockInfoTxn(block=block_info).SerializeToString() public_key = self._signer.get_public_key().as_hex() header = TransactionHeader( signer_public_key=public_key, family_name=FAMILY_NAME, family_version=FAMILY_VERSION, inputs=[CONFIG_ADDRESS, BLOCK_INFO_NAMESPACE], outputs=[CONFIG_ADDRESS, BLOCK_INFO_NAMESPACE], dependencies=[], payload_sha512=hashlib.sha512(payload).hexdigest(), batcher_public_key=public_key, ).SerializeToString() transaction_signature = self._signer.sign(header) transaction = Transaction( header=header, payload=payload, header_signature=transaction_signature, ) header = BatchHeader( signer_public_key=public_key, transaction_ids=[transaction_signature], ).SerializeToString() batch_signature = self._signer.sign(header) return Batch( header=header, transactions=[transaction], header_signature=batch_signature, )
def get_batch_by_transaction(self, transaction_id): """ Check to see if the requested transaction_id is in the current chain. If so, find the batch that has the transaction referenced by the transaction_id and return the batch. This is done by finding the block and searching for the batch. :param transaction_id (string): The id of the transaction that is being requested. :return: The batch that has the transaction. """ block = self.get_block_by_transaction_id(transaction_id) # Find batch in block for batch in block.batches: batch_header = BatchHeader() batch_header.ParseFromString(batch.header) if transaction_id in batch_header.transaction_ids: return batch
def _generate_batch_from_payload(self, payload): txn = self.generate_transaction(payload) batch_header = BatchHeader( signer_public_key=self.signer.get_public_key().as_hex(), transaction_ids=[txn.header_signature]).SerializeToString() batch = Batch(header=batch_header, header_signature=self.signer.sign(batch_header), transactions=[txn]) return batch
def validate_batch(batch): # validate batch signature header = BatchHeader() header.ParseFromString(batch.header) valid = signing.verify(batch.header, batch.header_signature, header.signer_pubkey) # validate all transactions in batch total = len(batch.transactions) index = 0 while valid and index < total: txn = batch.transactions[index] valid = validate_transaction(txn) if valid: txn_header = TransactionHeader() txn_header.ParseFromString(txn.header) if txn_header.batcher_pubkey != header.signer_pubkey: valid = False index += 1 return valid
def _generate_batch_from_payload(self, payload): txn = self.generate_transaction(payload) batch_header = BatchHeader(signer_pubkey=self.public_key, transaction_ids=[txn.header_signature ]).SerializeToString() batch = Batch(header=batch_header, header_signature=self._signed_header(batch_header), transactions=[txn]) LOGGER.debug("Generated %s", dumps_batch(batch)) return batch
def check_off_chain_batch_roles(self, batch): """ Check the batch signing key against the allowed off-chain transactor permissions. The roles being checked are the following, from first to last: "transactor.batch_signer" "transactor" The first role that is set will be the one used to enforce if the batch signer is allowed. Args: batch (Batch): The batch that is being verified. state_root(string): The state root of the previous block. If this is None, the current state root hash will be retrieved. """ if self._permissions is None: return True header = BatchHeader() header.ParseFromString(batch.header) policy = None if "transactor.batch_signer" in self._permissions: policy = self._permissions["transactor.batch_signer"] elif "transactor" in self._permissions: policy = self._permissions["transactor"] allowed = True if policy is not None: allowed = self._allowed(header.signer_public_key, policy) if allowed: return self.check_off_chain_transaction_roles(batch.transactions) LOGGER.debug( "Batch Signer: %s is not permitted by local" " configuration.", header.signer_public_key) return False
def _create_batches(self, batch_count, txn_count): batch_list = [] for _ in range(batch_count): txn_list = self._create_transactions(txn_count) txn_sig_list = [txn.header_signature for txn in txn_list] batch_header = BatchHeader( signer_public_key=self.signer.get_public_key().as_hex()) batch_header.transaction_ids.extend(txn_sig_list) header_bytes = batch_header.SerializeToString() signature = self.signer.sign(header_bytes) batch = Batch(header=header_bytes, transactions=txn_list, header_signature=signature) batch_list.append(batch) return batch_list
def send(self, transactions): """ Package up transactions into a batch and send them to the network via the provided batch_sender. :param transactions: list of transactions to package and broadcast. :return: None """ txn_signatures = [txn.header_signature for txn in transactions] header = BatchHeader( signer_pubkey=self._identity_public_key, transaction_ids=txn_signatures).SerializeToString() signature = signing.sign(header, self._identity_signing_key) batch = Batch(header=header, transactions=transactions, header_signature=signature) self._batch_sender.send(batch)
def create_batch(signer, triples): transactions = [ create_transaction(signer, verb, name, value) for verb, name, value in triples ] txn_signatures = [txn.header_signature for txn in transactions] header = BatchHeader(signer_public_key=signer.get_public_key().as_hex(), transaction_ids=txn_signatures).SerializeToString() signature = signer.sign(header) batch = Batch(header=header, transactions=transactions, header_signature=signature) batch_list = BatchList(batches=[batch]) return batch_list.SerializeToString()
def _create_batch(signer, transactions): """Creates a batch from a list of transactions and a public key, and signs the resulting batch with the given signing key. Args: signer (:obj:`Signer`): The cryptographic signer transactions (list of `Transaction`): The transactions to add to the batch. Returns: `Batch`: The constructed and signed batch. """ txn_ids = [txn.header_signature for txn in transactions] batch_header = BatchHeader( signer_public_key=signer.get_public_key().as_hex(), transaction_ids=txn_ids).SerializeToString() return Batch(header=batch_header, header_signature=signer.sign(batch_header), transactions=transactions, timestamp=int(time.time()))
def is_batch_signer_authorized(self, batch, state_root=None): """ Check the batch signing key against the allowed transactor permissions. The roles being checked are the following, from first to last: "transactor.batch_signer" "transactor" "default" The first role that is set will be the one used to enforce if the batch signer is allowed. Args: batch (Batch): The batch that is being verified. state_root(string): The state root of the previous block; if this is specified, do not read cached values. If this is None, the current state root hash and cached values will be used. """ if state_root is None: state_root_func = self._current_root_func from_state = False if not self._chain_head_set: if self._current_root_func() == INIT_ROOT_KEY: if not self._log_guard.chain_head_not_yet_set: LOGGER.debug("Chain head is not set yet. Permit all.") self._log_guard.chain_head_not_yet_set = True return True self._chain_head_set = True else: def state_root_func(): return state_root from_state = True self._cache.update_view(state_root_func()) header = BatchHeader() header.ParseFromString(batch.header) role = self._cache.get_role("transactor.batch_signer", state_root_func, from_state) if role is None: role = self._cache.get_role("transactor", state_root_func, from_state) if role is None: policy_name = "default" else: policy_name = role.policy_name policy = self._cache.get_policy(policy_name, state_root_func, from_state) if policy is None: allowed = True else: allowed = self._allowed(header.signer_public_key, policy) if allowed: return self.is_transaction_signer_authorized( batch.transactions, state_root_func, from_state) LOGGER.debug("Batch Signer: %s is not permitted.", header.signer_public_key) return False