def setUp(self): self.dir = tempfile.mkdtemp() self.block_db = NativeLmdbDatabase( os.path.join(self.dir, 'block.lmdb'), BlockStore.create_index_configuration()) self.block_store = BlockStore(self.block_db) self.block_manager = BlockManager() self.block_manager.add_commit_store(self.block_store) self.gossip = MockGossip() self.completer = Completer( block_manager=self.block_manager, transaction_committed=self.block_store.has_transaction, get_committed_batch_by_id=self.block_store.get_batch, get_committed_batch_by_txn_id=( self.block_store.get_batch_by_transaction ), get_chain_head=lambda: self.block_store.chain_head, gossip=self.gossip) self.completer.set_on_block_received(self._on_block_received) self.completer.set_on_batch_received(self._on_batch_received) self._has_block_value = True context = create_context('secp256k1') private_key = context.new_random_private_key() crypto_factory = CryptoFactory(context) self.signer = crypto_factory.new_signer(private_key) self.blocks = [] self.batches = []
def __init__(self, delegate, args): super(IntKeyWorkload, self).__init__(delegate, args) self._auth_info = args.auth_info self._urls = [] self._pending_batches = {} self._lock = threading.Lock() self._delegate = delegate self._deps = {} context = create_context('secp256k1') crypto_factory = CryptoFactory(context=context) if args.key_file is not None: try: with open(args.key_file, 'r') as infile: signing_key = infile.read().strip() private_key = Secp256k1PrivateKey.from_hex(signing_key) self._signer = crypto_factory.new_signer( private_key=private_key) except ParseError as pe: raise IntKeyCliException(str(pe)) except IOError as ioe: raise IntKeyCliException(str(ioe)) else: self._signer = crypto_factory.new_signer( context.new_random_private_key())
def _read_signer(key_filename): """Reads the given file as a hex key. Args: key_filename: The filename where the key is stored. If None, defaults to the default key for the current user. Returns: Signer: the signer Raises: CliException: If unable to read the file. """ filename = key_filename if filename is None: filename = os.path.join(os.path.expanduser('~'), '.sawtooth', 'keys', getpass.getuser() + '.priv') try: with open(filename, 'r') as key_file: signing_key = key_file.read().strip() except IOError as e: raise CliException('Unable to read key file: {}'.format(str(e))) try: private_key = Secp256k1PrivateKey.from_hex(signing_key) except ParseError as e: raise CliException('Unable to read key in file: {}'.format(str(e))) context = create_context('secp256k1') crypto_factory = CryptoFactory(context) return crypto_factory.new_signer(private_key)
def do_populate(batches, keys): context = create_context('secp256k1') private_key = context.new_random_private_key() crypto_factory = CryptoFactory(context) signer = crypto_factory.new_signer(private_key) total_txn_count = 0 txns = [] for i in range(0, len(keys)): name = list(keys)[i] txn = create_intkey_transaction( verb='set', name=name, value=random.randint(9000, 100000), deps=[], signer=signer) total_txn_count += 1 txns.append(txn) # Establish the signature of the txn associated with the word # so we can create good dependencies later keys[name] = txn.header_signature batch = create_batch( transactions=txns, signer=signer) batches.append(batch)
def setUp(self): context = create_context('secp256k1') crypto_factory = CryptoFactory(context) private_key = context.new_random_private_key() self.signer = crypto_factory.new_signer(private_key) self._identity_view_factory = MockIdentityViewFactory() self.permissions = {} self._identity_cache = IdentityCache( self._identity_view_factory) self.permission_verifier = \ PermissionVerifier( permissions=self.permissions, current_root_func=self._current_root_func, identity_cache=self._identity_cache)
def __init__(self, with_genesis=True): self.block_sender = MockBlockSender() self.batch_sender = MockBatchSender() self.dir = tempfile.mkdtemp() self.block_db = NativeLmdbDatabase( os.path.join(self.dir, 'block.lmdb'), BlockStore.create_index_configuration()) self.block_store = BlockStore(self.block_db) self.block_cache = BlockCache(self.block_store) self.state_db = NativeLmdbDatabase( os.path.join(self.dir, "merkle.lmdb"), MerkleDatabase.create_index_configuration()) self.state_view_factory = NativeStateViewFactory(self.state_db) self.block_manager = BlockManager() self.block_manager.add_commit_store(self.block_store) context = create_context('secp256k1') private_key = context.new_random_private_key() crypto_factory = CryptoFactory(context) self.signer = crypto_factory.new_signer(private_key) identity_private_key = context.new_random_private_key() self.identity_signer = crypto_factory.new_signer(identity_private_key) chain_head = None if with_genesis: self.genesis_block = self.generate_genesis_block() chain_head = self.genesis_block self.block_manager.put([chain_head.block]) self.block_manager.persist( chain_head.block.header_signature, "commit_store") self.block_publisher = BlockPublisher( block_manager=self.block_manager, transaction_executor=MockTransactionExecutor(), transaction_committed=self.block_store.has_transaction, batch_committed=self.block_store.has_batch, state_view_factory=self.state_view_factory, block_sender=self.block_sender, batch_sender=self.block_sender, chain_head=chain_head.block, identity_signer=self.identity_signer, data_dir=None, config_dir=None, permission_verifier=MockPermissionVerifier(), batch_observers=[])
def __init__(self, base_url, keyfile, wait=None): """ Member variables: _base_url _private_key _public_key _transaction_family _family_version _wait """ self._base_url = base_url try: with open(keyfile) as fd: private_key_str = fd.read().strip() except OSError as err: raise IOError("Failed to read keys: {}.".format(str(err))) try: private_key = Secp256k1PrivateKey.from_hex(private_key_str) except ParseError as e: raise BattleshipException( 'Unable to load private key: {}'.format(str(e))) self._signer = CryptoFactory( create_context('secp256k1')).new_signer(private_key) self._transaction_family = "battleship" self._family_version = "1.0" self._wait = wait
def setUp(self): self.block_store = BlockStore(DictDatabase( indexes=BlockStore.create_index_configuration())) self.gossip = MockGossip() self.completer = Completer(self.block_store, self.gossip) self.completer._on_block_received = self._on_block_received self.completer._on_batch_received = self._on_batch_received self.completer._has_block = self._has_block self._has_block_value = True context = create_context('secp256k1') private_key = context.new_random_private_key() crypto_factory = CryptoFactory(context) self.signer = crypto_factory.new_signer(private_key) self.blocks = [] self.batches = []
def __init__(self, with_genesis=True): self.block_sender = MockBlockSender() self.batch_sender = MockBatchSender() self.block_store = BlockStore(DictDatabase( indexes=BlockStore.create_index_configuration())) self.block_cache = BlockCache(self.block_store) self.state_db = {} # add the mock reference to the consensus consensus_setting_addr = SettingsView.setting_address( 'sawtooth.consensus.algorithm') self.state_db[consensus_setting_addr] = _setting_entry( 'sawtooth.consensus.algorithm', 'test_journal.mock_consensus') self.state_view_factory = MockStateViewFactory(self.state_db) context = create_context('secp256k1') private_key = context.new_random_private_key() crypto_factory = CryptoFactory(context) self.signer = crypto_factory.new_signer(private_key) identity_private_key = context.new_random_private_key() self.identity_signer = crypto_factory.new_signer(identity_private_key) chain_head = None if with_genesis: self.genesis_block = self.generate_genesis_block() self.set_chain_head(self.genesis_block) chain_head = self.genesis_block self.block_publisher = BlockPublisher( transaction_executor=MockTransactionExecutor(), block_cache=self.block_cache, state_view_factory=self.state_view_factory, settings_cache=SettingsCache( SettingsViewFactory(self.state_view_factory), ), block_sender=self.block_sender, batch_sender=self.block_sender, squash_handler=None, chain_head=chain_head, identity_signer=self.identity_signer, data_dir=None, config_dir=None, permission_verifier=MockPermissionVerifier(), check_publish_block_frequency=0.1, batch_observers=[])
async def create_account(request): """Creates a new Account and corresponding authorization token""" required_fields = ['email', 'password'] common.validate_fields(required_fields, request.json) private_key = request.app.config.CONTEXT.new_random_private_key() signer = CryptoFactory(request.app.config.CONTEXT).new_signer(private_key) public_key = signer.get_public_key().as_hex() auth_entry = _create_auth_dict( request, public_key, private_key.as_hex()) await auth_query.create_auth_entry(request.app.config.DB_CONN, auth_entry) account = _create_account_dict(request.json, public_key) batches, batch_id = transaction_creation.create_account( txn_key=signer, batch_key=request.app.config.SIGNER, label=account.get('label'), description=account.get('description')) await messaging.send( request.app.config.VAL_CONN, request.app.config.TIMEOUT, batches) try: await messaging.check_batch_status( request.app.config.VAL_CONN, batch_id) except (ApiBadRequest, ApiInternalError) as err: await auth_query.remove_auth_entry( request.app.config.DB_CONN, request.json.get('email')) raise err token = common.generate_auth_token( request.app.config.SECRET_KEY, account.get('email'), public_key) return response.json( { 'authorization': token, 'account': account })
def do_generate(args, batches, keys): context = create_context('secp256k1') private_key = context.new_random_private_key() crypto_factory = CryptoFactory(context) signer = crypto_factory.new_signer(private_key) start = time.time() total_txn_count = 0 for i in range(0, args.count): txns = [] for _ in range(0, random.randint(1, args.max_batch_size)): name = random.choice(list(keys)) txn = create_intkey_transaction( verb=random.choice(['inc', 'dec']), name=name, value=random.randint(1, 10), deps=[keys[name]], signer=signer) total_txn_count += 1 txns.append(txn) batch = create_batch( transactions=txns, signer=signer) batches.append(batch) if i % 100 == 0 and i != 0: stop = time.time() txn_count = 0 for batch in batches[-100:]: txn_count += len(batch.transactions) fmt = 'batches {}, batch/sec: {:.2f}, txns: {}, txns/sec: {:.2f}' print(fmt.format( str(i), 100 / (stop - start), str(total_txn_count), txn_count / (stop - start))) start = stop
def test_authorization_challenge_submit_bad_signature(self): """ Test the AuthorizationChallengeSubmitHandler returns an AuthorizationViolation and closes the connection if the signature is not verified. """ context = create_context('secp256k1') private_key = context.new_random_private_key() crypto_factory = CryptoFactory(context) signer = crypto_factory.new_signer(private_key) payload = os.urandom(10) signature = signer.sign(payload) auth_challenge_submit = AuthorizationChallengeSubmit( public_key="other", signature=signature, roles=[RoleType.Value("NETWORK")]) roles = {"network": AuthorizationType.TRUST} network = MockNetwork( roles, connection_status={ "connection_id": ConnectionStatus.AUTH_CHALLENGE_REQUEST }) permission_verifer = MockPermissionVerifier() gossip = MockGossip() handler = AuthorizationChallengeSubmitHandler( network, permission_verifer, gossip, {"connection_id": payload}) handler_status = handler.handle( "connection_id", auth_challenge_submit.SerializeToString()) self.assertEqual(handler_status.status, HandlerStatus.RETURN_AND_CLOSE) self.assertEqual( handler_status.message_type, validator_pb2.Message.AUTHORIZATION_VIOLATION)
def load_identity_signer(key_dir, key_name): """Loads a private key from the key directory, based on a validator's identity. Args: key_dir (str): The path to the key directory. key_name (str): The name of the key to load. Returns: Signer: the cryptographic signer for the key """ key_path = os.path.join(key_dir, '{}.priv'.format(key_name)) if not os.path.exists(key_path): raise LocalConfigurationError( "No such signing key file: {}".format(key_path)) if not os.access(key_path, os.R_OK): raise LocalConfigurationError( "Key file is not readable: {}".format(key_path)) LOGGER.info('Loading signing key: %s', key_path) try: with open(key_path, 'r') as key_file: private_key_str = key_file.read().strip() except IOError as e: raise LocalConfigurationError( "Could not load key file: {}".format(str(e))) try: private_key = Secp256k1PrivateKey.from_hex(private_key_str) except signing.ParseError as e: raise LocalConfigurationError( "Invalid key in file {}: {}".format(key_path, str(e))) context = signing.create_context('secp256k1') crypto_factory = CryptoFactory(context) return crypto_factory.new_signer(private_key)
def create_chain(num=10): context = create_context('secp256k1') private_key = context.new_random_private_key() crypto_factory = CryptoFactory(context) signer = crypto_factory.new_signer(private_key) counter = 1 previous_block_id = "0000000000000000" blocks = [] while counter <= num: current_block_id = uuid4().hex txns = [ t[0] for t in [ create_transaction( payload=uuid4().hex.encode(), signer=signer) for _ in range(20) ] ] txn_ids = [t.header_signature for t in txns] batch = create_batch( transactions=txns, signer=signer) blk_w = create_block( counter, previous_block_id, current_block_id, batches=[batch]) blocks.append((current_block_id, blk_w, txn_ids)) counter += 1 previous_block_id = current_block_id return blocks
def __init__(self, url, keyfile=None): self.url = url if keyfile is not None: try: with open(keyfile) as fd: private_key_str = fd.read().strip() fd.close() except OSError as err: raise IntkeyClientException( 'Failed to read private key: {}'.format(str(err))) try: private_key = Secp256k1PrivateKey.from_hex(private_key_str) except ParseError as e: raise IntkeyClientException( 'Unable to load private key: {}'.format(str(e))) self._signer = CryptoFactory( create_context('secp256k1')).new_signer(private_key)
def __init__(self, file_name): """ Args: file_name (str): The yaml filename and path. scheduler (scheduler.Scheduler): Any Scheduler implementaion context_manager (context_manager.ContextManager): The context manager holding state for this scheduler. """ self._context = create_context('secp256k1') self._crypto_factory = CryptoFactory(self._context) self._yaml_file_name = file_name self._counter = itertools.count(0) self._referenced_txns_in_other_batches = {} self._batch_id_by_txn_id = {} self._txn_execution = {} self._batch_results = {} self._batches = [] self._create_batches()
def __init__(self, name, rest_endpoint): """ Args: name (str): An identifier for this Transactor rest_endpoint (str): The rest api that this Transactor will communicate with. """ self.name = name self._rest_endpoint = rest_endpoint \ if rest_endpoint.startswith("http://") \ else "http://{}".format(rest_endpoint) with open('/root/.sawtooth/keys/{}.priv'.format(name)) as priv_file: private_key = Secp256k1PrivateKey.from_hex( priv_file.read().strip('\n')) self._signer = CryptoFactory(create_context('secp256k1')) \ .new_signer(private_key) self._factories = {} self._client = RestClient(url=self._rest_endpoint) self._add_transaction_family_factory(Families.INTKEY) self._add_transaction_family_factory(Families.XO)
def __init__(self, base_url, keyfile=None): self._base_url = base_url if keyfile is None: self._signer = None return try: with open(keyfile) as fd: private_key_str = fd.read().strip() except OSError as err: raise XoException( 'Failed to read private key {}: {}'.format( keyfile, str(err))) try: private_key = Secp256k1PrivateKey.from_hex(private_key_str) except ParseError as e: raise XoException( 'Unable to load private key: {}'.format(str(e))) self._signer = CryptoFactory(create_context('secp256k1')) \ .new_signer(private_key)
class CodeSmellClient: """ construct and send code smell transaction. """ def __init__(self, base_url, work_path, keyfile=None): self._base_url = base_url self._work_path = work_path if keyfile is None: self._signer = None return try: with open(keyfile) as fileptr: private_key_str = fileptr.read().strip() except OSError as err: raise CodeSmellException( 'Failed to read private key {}: {}'.format(keyfile, str(err))) try: private_key = Secp256k1PrivateKey.from_hex(private_key_str) except ParseError as error: raise CodeSmellException('Unable to load private key: {}'.format( str(error))) self._signer = CryptoFactory( create_context('secp256k1')).new_signer(private_key) def default(self, repo_id=None): """ load a defautl code smell configuration """ #identify code_smell family configuration file conf_file = self._work_path + '/etc/.suse' response = "" #get date txn_date = _get_date() #return txn_date if os.path.isfile(conf_file): try: with open(conf_file) as config: raw_config = config.read() except IOError as error: raise CodeSmellException( "Unable to load code smell family configuration file: {}". format(error)) #load toml config into a dict parsed_toml_config = toml.loads(raw_config) #get default code smells code_smells_config = parsed_toml_config['code_smells'] """traverse dict and process each code smell nested for loop to procces level two dict.""" for code_smells in code_smells_config.values(): for name, metric in code_smells.items(): #send trasaction response = self._send_code_smell_txn( txn_type='code_smell', txn_id=name, data=str(metric[0]), ## TODO: add weigth value state='create', date=txn_date) code_smells_config = parsed_toml_config['vote_setting'] """traverse dict and process each code smell nested for loop to procces level two dict.""" for name, metric in code_smells_config.items(): #send transaction response = self._send_code_smell_txn(txn_type='code_smell', txn_id=name, data=str(metric), state='create', date=txn_date) else: raise CodeSmellException( "Configuration File {} does not exists".format(conf_file)) #send configuration file to all peers self._publish_config(conf_file=conf_file) #send new config to github suse_config = _get_suse_config(conf_file) self._send_git_request(suse_config, repo_id) return response def list(self, txn_type=None, active=None): """ list all transactions. Args: type (str), asset that we want to list (code smells, proposals, votes) """ #pull all transactions of code smell family result = self._send_request("transactions") transactions = {} try: proposal_info = " " encoded_entries = yaml.safe_load(result)["data"] if txn_type is None: for entry in encoded_entries: transactions[entry["header_signature"]] = base64.b64decode( entry["payload"]) else: for entry in encoded_entries: #if the transaction does not have the expected format ignore it try: transaction_type = base64.b64decode( entry["payload"]).decode().split(',')[0] if transaction_type == txn_type: if txn_type == "proposal" and active is not None: status = base64.b64decode( entry["payload"]).decode().split(',')[3] if status == "active": transactions[entry[ "header_signature"]] = base64.b64decode( entry["payload"]) p_date = base64.b64decode( entry["payload"]).decode().split( ',')[4] proposal_info = entry[ "header_signature"] + " " + p_date break else: transactions[entry[ "header_signature"]] = base64.b64decode( entry["payload"]) except: pass if proposal_info != " ": return proposal_info else: return transactions except BaseException: return None def show(self, address): """ list a specific transaction, based on its address Args: address (str), transaction's address """ result = self._send_request("transactions/{}".format(address)) transactions = {} try: encoded_entries = yaml.safe_load(result)["data"] transactions["payload"] = base64.b64decode( encoded_entries["payload"]) transactions["header_signature"] = encoded_entries[ "header_signature"] return transactions except BaseException: return None def propose(self, code_smells): """ propose new metrics for the code smell families the function assumes that all code smells have been updated even if they don't Args: code_smells (dict), dictionary of code smells and metrics """ #get code smell family address prefix code_smell_prefix = self._get_prefix() #check for an active proposal, transactions are return in sequential order. proposal_result = self._send_request( "state?address={}".format(code_smell_prefix)) encoded_entries = yaml.safe_load(proposal_result)["data"] print(encoded_entries) try: for entry in encoded_entries: print(base64.b64decode(entry["data"]).decode().split(',')[0]) #look for the first proposal transactiosn if base64.b64decode( entry["data"]).decode().split(',')[0] == "proposal": last_proposal = base64.b64decode( entry["data"]).decode().split(',') break print(last_proposal) #try: #if last_proposal[3] == "active": # return "Invalid Operation, another proposal is Active" except BaseException: pass #get date propose_date = _get_date() #send transaction response = self._send_code_smell_txn( txn_id=_sha512(str(code_smells).encode('utf-8'))[0:6], txn_type='proposal', data=str(code_smells).replace(",", ";"), state='active', date=propose_date) return response def update_proposal(self, proposal_id, state, repo_id): """ update proposal state Args: proposal_id (str), proposal ID state (Str), new proposal ID """ proposal = self.show(proposal_id) if state == 1: state = "Accepted" else: state = "Rejected" self._update_proposal(proposal, state, repo_id) def _update_proposal(self, proposal, state, repo_id): """ update proposal, update state. proposal state 1 = accepeted, 0 = rejected Args: proposal (dict), proposal data sate (str), new proposal's state """ conf_file = self._work_path + 'etc/.suse' txn_date = _get_date() proposal = proposal["payload"].decode().split(',') response = self._send_code_smell_txn(txn_id=proposal[1], txn_type='proposal', data=proposal[2], state=str(state), date=txn_date) print(response) if state == "Accepted": #update suse configuration file self._update_suse_file(proposal) #send new config to github suse_config = _get_suse_config(conf_file) self._send_git_request(suse_config, repo_id) def _send_git_request(self, toml_config, repo_id=None): """ send new code smell configuration to github Args: toml_config (dict): code smells to send """ wrapper_json = {} wrapper_json["sender"] = "Sawtooth" wrapper_json["repo"] = repo_id wrapper_json["suse_file"] = toml_config data = json.dumps(wrapper_json) requests.post('http://129.108.7.2:3000', data=data) def _update_suse_file(self, proposal): """ update code smell configuration metrics, after the proposal is accepted the configuration file needs to be updated. Args: toml_config (dict), current configuration proposal (str), proposal that contains new configuration """ #get proposal payload proposal_payload = yaml.safe_load(proposal[2].replace(";", ",")) #identify code_smell family configuration file conf_file = self._work_path + '/etc/.suse' print(self._work_path) #get current config suse_config = _get_suse_config(conf_file) """ start by traversing the proposal, get the code smell and the metric """ try: for proposal_key, proposal_metric in proposal_payload.items(): tmp_type = "" """ we don't know where on the toml file is the code smell, traverse the toml dictionary looking for the same code smell. """ for code_type in suse_config["code_smells"]: """ once you found the code smell, break the loop and return a pseudo location """ if proposal_key in suse_config["code_smells"][ code_type].keys(): tmp_type = code_type break #update code smell metric if proposal_key == "CommentsToCodeRatioLower" or proposal_key == "CommentsToCodeRatioUpper": suse_config["code_smells"][tmp_type][proposal_key][ 0] = float(proposal_metric) else: suse_config["code_smells"][tmp_type][proposal_key][ 0] = int(proposal_metric) #save new configuration try: with open(conf_file, 'w+') as config: toml.dump(suse_config, config) #self._send_git_request(toml_config) except IOError as error: raise CodeSmellException( "Unable to open configuration file {}".format(error)) #publish new configuration file to all peers self._publish_config(conf_file=conf_file) except: raise CodeSmellException("Incorrect proposal format") def check_votes(self, proposal_id): """ review the votes of a proposal Args: proposal_id (str), proposal id """ #print(self._base_url) result = self._send_request("transactions/{}".format(proposal_id)) #print (proposal_id) #print ("print"+result) encoded_result = yaml.safe_load(result)["data"] proposal = base64.b64decode( encoded_result["payload"]).decode().split(',') proposal_id = proposal[1] transactions = self.list(txn_type='vote') votes = [] if not transactions: return "" for vote in transactions: #for all votes of proposal if transactions[vote].decode().split(',')[2] == proposal_id: votes.append(int(transactions[vote].decode().split(',')[3])) return votes def vote(self, proposal_id, vote): """ vote to accept or reject a proposal Args: proposal_id (str), id of proposal vote (int), value of vote 1=accept, 0=reject """ #verify active proposal result = self._send_request("transactions/{}".format(proposal_id)) encoded_result = yaml.safe_load(result)["data"] proposal = base64.b64decode( encoded_result["payload"]).decode().split(',') if proposal[3] != 'active': return "Proposal not active" #The condition to only one vote will be handle from the GUI. #leaving this logic for future reference #verify double voting # proposal_id = proposal[1] # result = self._send_request("transactions") # encoded_entries = yaml.safe_load(result)["data"] # for entry in encoded_entries: # transaction_type = base64.b64decode(entry["payload"]).decode().split(',')[0] # if transaction_type == 'vote': # if entry['header']['signer_public_key'] == self._signer.get_public_key().as_hex(): # return ("User already submitted a vote") txn_date = _get_date() #active proposal, record vote response = self._send_code_smell_txn(txn_id=str( random.randrange(1, 99999)), txn_type='vote', data=proposal[1], state=str(vote), date=txn_date) return response def _publish_config(self, conf_file=None): """ function to send an update configuration transaction to the chain after the code smell configuration is updated all peers in the network must update the local configuration file. Args: config (dictionary), code smell configuration """ #read .suse configuration file suse_config = _get_suse_config(conf_file) #get current time txn_date = _get_date() self._send_code_smell_txn(txn_id=str(random.randrange(1, 99999)), txn_type='config', data=str(suse_config).replace(",", ";"), state='update', date=txn_date) def _get_prefix(self): """ get code smell family address prefix """ return _sha512('code-smell'.encode('utf-8'))[0:6] def _get_address(self, transaction_id): """ get transaction address Args: id (str): trasaction id """ code_smell_prefix = self._get_prefix() code_smell_address = _sha512(transaction_id.encode('utf-8'))[0:64] return code_smell_prefix + code_smell_address def _send_request(self, suffix, data=None, content_type=None, auth_user=None, auth_password=None): """ send request to code smell processor the transaction will be validate by the processor of each family. """ if self._base_url.startswith("http://"): url = "{}/{}".format(self._base_url, suffix) else: url = "http://{}/{}".format(self._base_url, suffix) headers = {} if auth_user is not None: auth_string = "{}:{}".format(auth_user, auth_password) b64_string = b64encode(auth_string.encode()).decode() auth_header = 'Basic {}'.format(b64_string) headers['authorization'] = auth_header if content_type is not None: headers['Content-Type'] = content_type try: if data is not None: result = requests.post(url, headers=headers, data=data) else: result = requests.get(url, headers=headers) if result.status_code == 404: raise CodeSmellException("No such transaction") elif not result.ok: raise CodeSmellException("Error {}:{}".format( result.status_code, result.reason)) except requests.ConnectionError as err: raise CodeSmellException('Failed to connect to {}:{}'.format( url, str(err))) except BaseException as err: raise CodeSmellException(err) return result.text def _send_code_smell_txn(self, txn_type=None, txn_id=None, data=None, state=None, date=None): """ serialize payload and create header transaction Args: type (str): type of transaction id (str): asset id, will depend on type of transaction data (object): transaction data state (str): all transactions must have a state wait (int): delay to process transactions """ #serialization is just a delimited utf-8 encoded strings if txn_type in ('proposal', 'config', 'code_smell', 'vote'): payload = ",".join([txn_type, txn_id, data, state, str(date)]).encode() else: payload = ",".join([txn_type, txn_id, data, state]).encode() #pprint("payload: {}".format(payload)) #construct the address address = self._get_address(txn_id) #construct header` header = TransactionHeader( signer_public_key=self._signer.get_public_key().as_hex(), family_name="code-smell", family_version="0.1", inputs=[address], outputs=[address], dependencies=[], payload_sha512=_sha512(payload), batcher_public_key=self._signer.get_public_key().as_hex(), nonce=hex(random.randint(0, 2**64))).SerializeToString() signature = self._signer.sign(header) #create transaction transaction = Transaction(header=header, payload=payload, header_signature=signature) #create batch list, suserum policy: one transaction per batch batch_list = self._create_batch_list([transaction]) return self._send_request("batches", batch_list.SerializeToString(), 'application/octet-stream') def _create_batch_list(self, transactions): """ Create the list of batches that the client will send to the REST API Args: transactions (transaction): transaction(s) included in the batch Returns: BatchList: a list of batches to send to the REST API """ transaction_signatures = [t.header_signature for t in transactions] header = BatchHeader( signer_public_key=self._signer.get_public_key().as_hex(), transaction_ids=transaction_signatures).SerializeToString() signature = self._signer.sign(header) batch = Batch(header=header, transactions=transactions, header_signature=signature) return BatchList(batches=[batch])
class SimpleWalletClient(object): '''Client simple wallet class. This supports deposit, withdraw, transfer, and balance functions. ''' def __init__(self, baseUrl, keyFile=None): '''Initialize the client class. This is mainly getting the key pair and computing the address. ''' self._baseUrl = baseUrl if keyFile is None: self._signer = None return try: with open(keyFile) as fd: privateKeyStr = fd.read().strip() except OSError as err: raise Exception('Failed to read private key {}: {}'.format( keyFile, str(err))) try: privateKey = Secp256k1PrivateKey.from_hex(privateKeyStr) except ParseError as err: raise Exception('Failed to load private key: {}'.format(str(err))) self._signer = CryptoFactory(create_context('secp256k1')) \ .new_signer(privateKey) self._publicKey = self._signer.get_public_key().as_hex() self._address = _hash(FAMILY_NAME.encode('utf-8'))[0:6] + \ _hash(self._publicKey.encode('utf-8'))[0:64] # For each valid cli command in _cli.py file, # add methods to: # 1. Do any additional handling, if required # 2. Create a transaction and a batch # 2. Send to rest-api def deposit(self, value): return self._wrap_and_send("deposit", value) def withdraw(self, value): try: retValue = self._wrap_and_send("withdraw", value) except Exception: raise Exception('Encountered an error during withdrawal') return retValue def make_driver(self, value): try: retValue = self._wrap_and_send("makeDriver", value) except Exception: raise Exception('Encountered an error during make driver') return retValue def transfer(self, value, clientToKey): try: with open(clientToKey) as fd: publicKeyStr = fd.read().strip() retValue = self._wrap_and_send("transfer", value, publicKeyStr) except OSError as err: raise Exception('Failed to read public key {}: {}'.format( clientToKey, str(err))) except Exception as err: raise Exception('Encountered an error during transfer', err) return retValue def balance(self): result = self._send_to_restapi("state/{}".format(self._address)) try: return base64.b64decode(yaml.safe_load(result)["data"]) except BaseException: return None def _send_to_restapi(self, suffix, data=None, contentType=None): '''Send a REST command to the Validator via the REST API.''' if self._baseUrl.startswith("http://"): url = "{}/{}".format(self._baseUrl, suffix) else: url = "http://{}/{}".format(self._baseUrl, suffix) headers = {} if contentType is not None: headers['Content-Type'] = contentType try: if data is not None: result = requests.post(url, headers=headers, data=data) else: result = requests.get(url, headers=headers) if not result.ok: raise Exception("Error {}: {}".format(result.status_code, result.reason)) except requests.ConnectionError as err: raise Exception('Failed to connect to {}: {}'.format( url, str(err))) except BaseException as err: raise Exception(err) return result.text def _wrap_and_send(self, action, *values): '''Create a transaction, then wrap it in a batch. Even single transactions must be wrapped into a batch. ''' # Generate a csv utf-8 encoded string as payload rawPayload = action for val in values: rawPayload = ",".join([rawPayload, str(val)]) payload = rawPayload.encode() # Construct the address where we'll store our state address = self._address inputAddressList = [address] outputAddressList = [address] if "transfer" == action: toAddress = _hash(FAMILY_NAME.encode('utf-8'))[0:6] + \ _hash(values[1].encode('utf-8'))[0:64] inputAddressList.append(toAddress) outputAddressList.append(toAddress) # Create a TransactionHeader header = TransactionHeader( signer_public_key=self._publicKey, family_name=FAMILY_NAME, family_version="1.0", inputs=inputAddressList, outputs=outputAddressList, dependencies=[], payload_sha512=_hash(payload), batcher_public_key=self._publicKey, nonce=random.random().hex().encode()).SerializeToString() # Create a Transaction from the header and payload above transaction = Transaction(header=header, payload=payload, header_signature=self._signer.sign(header)) transactionList = [transaction] # Create a BatchHeader from transactionList above header = BatchHeader( signer_public_key=self._publicKey, transaction_ids=[txn.header_signature for txn in transactionList]).SerializeToString() #Create Batch using the BatchHeader and transactionList above batch = Batch(header=header, transactions=transactionList, header_signature=self._signer.sign(header)) #Create a Batch List from Batch above batch_list = BatchList(batches=[batch]) # Send batch_list to rest-api return self._send_to_restapi("batches", batch_list.SerializeToString(), 'application/octet-stream')
def test_block_publisher_doesnt_finalize_block( self, mock_utils, mock_validator_registry_view, mock_consensus_state, mock_poet_enclave_factory, mock_consensus_state_store, mock_poet_key_state_store, mock_signup_info, mock_wait_certificate, mock_poet_settings_view, mock_block_wrapper): """ Test verifies that PoET Block Publisher doesn't finalize a candidate block that doesn't have a valid wait certificate. """ # create a mock_validator_registry_view with # get_validator_info that does nothing mock_validator_registry_view.return_value.get_validator_info. \ return_value = \ ValidatorInfo( name='validator_001', id='validator_deadbeef', signup_info=SignUpInfo( poet_public_key='00112233445566778899aabbccddeeff')) # create a mock_wait_certificate that pretends to fail mock_wait_certificate.create_wait_certificate.side_effect = \ ValueError('Unit test fake failure') # create a mock_consensus_state that returns a mock with # the following settings: mock_state = MockConsensusState().create_mock_consensus_state() mock_consensus_state.consensus_state_for_block_id.return_value = \ mock_state # create mock_batch_publisher context = create_context('secp256k1') private_key = context.new_random_private_key() crypto_factory = CryptoFactory(context) signer = crypto_factory.new_signer(private_key) mock_batch_publisher = mock.Mock( identity_signer=signer) mock_block_cache = mock.MagicMock() mock_state_view_factory = mock.Mock() # create mock_block_header with the following fields mock_block = mock.Mock(identifier='0123456789abcdefedcba9876543210') mock_block.header.signer_public_key = \ '90834587139405781349807435098745' mock_block.header.previous_block_id = '2' mock_block.header.block_num = 1 mock_block.header.state_root_hash = '6' mock_block.header.batch_ids = '4' # check test with mock.patch('sawtooth_poet.poet_consensus.poet_block_publisher.' 'LOGGER') as mock_logger: block_publisher = \ poet_block_publisher.PoetBlockPublisher( block_cache=mock_block_cache, state_view_factory=mock_state_view_factory, batch_publisher=mock_batch_publisher, data_dir=self._temp_dir, config_dir=self._temp_dir, validator_id='validator_deadbeef') with mock.patch('sawtooth_poet.poet_consensus.' 'poet_block_publisher.json') as _: self.assertFalse( block_publisher.finalize_block( block_header=mock_block.header)) # Could be a hack, but verify that the appropriate log message is # generated - so we at least have some faith that the failure was # because of what we are testing and not something else. I know # that this is fragile if the log message is changed, so would # accept any suggestions on a better way to verify that the # function fails for the reason we expect. (message, *_), _ = mock_logger.error.call_args self.assertTrue('Failed to create wait certificate: ' in message)
import agpayload_pb2 import enums_pb2 from hashlib import sha512 from sawtooth_sdk.protobuf.transaction_pb2 import TransactionHeader from sawtooth_sdk.protobuf.transaction_pb2 import Transaction from sawtooth_sdk.protobuf.batch_pb2 import BatchHeader,Batch,BatchList from sawtooth_signing import create_context from sawtooth_signing import CryptoFactory import urllib.request from urllib.error import HTTPError import addresser context = create_context('secp256k1') private_key = context.new_random_private_key() signer = CryptoFactory(context).new_signer(private_key) public_key = signer.get_public_key().as_hex() payload = agpayload_pb2.Realpayload() payload.Action = agpayload_pb2.action.Value('register_farmer') payload.reg_far.aadhar_card = 'dfv98fsdff98s83' payload.reg_far.timestamp = 12242343 payload.reg_far.full_name = 'Bro Code' payload.reg_far.otp = 1234 payload.reg_far.State = enums_pb2.state.Value('Gujarat') payload.reg_far.pincode = 123456 payload.reg_far.mobilenumber = 9999999999 payload.reg_far.district = 'sachin' input = [addresser.get_farmer_address(public_key),addresser.get_otp_address(9999999999,1234)] print(input) payload_bytes = payload.SerializeToString()
def __init__(self, validator_url): self._connection = Connection(validator_url) self._context = create_context('secp256k1') self._crypto_factory = CryptoFactory(self._context) self._batch_signer = self._crypto_factory.new_signer( self._context.new_random_private_key())
class BattleshipClient: def __init__(self, base_url, keyfile, wait=None): """ Member variables: _base_url _private_key _public_key _transaction_family _family_version _wait """ self._base_url = base_url try: with open(keyfile) as fd: private_key_str = fd.read().strip() except OSError as err: raise IOError("Failed to read keys: {}.".format(str(err))) try: private_key = Secp256k1PrivateKey.from_hex(private_key_str) except ParseError as e: raise BattleshipException( 'Unable to load private key: {}'.format(str(e))) self._signer = CryptoFactory( create_context('secp256k1')).new_signer(private_key) self._transaction_family = "battleship" self._family_version = "1.0" self._wait = wait def _send_battleship_txn(self, update): """The client needs to have the same defaults as the Transaction subclass before it is signed inside sendtxn """ if 'Name' not in update: raise BattleshipException('Game name required') if 'Action' not in update: update['Action'] = None if 'Ships' not in update: update['Ships'] = None if update['Action'] == 'JOIN': if 'Board' not in update: update['Board'] = None if update['Action'] == 'FIRE': if 'Column' not in update: update['Column'] = None if 'Row' not in update: update['Row'] = None payload = json.dumps(update).encode() address = self._get_address(update['Name']) header = TransactionHeader( signer_public_key=self._signer.get_public_key().as_hex(), family_name=self._transaction_family, family_version=self._family_version, inputs=[address], outputs=[address], dependencies=[], payload_sha512=self._sha512(payload), batcher_public_key=self.signer.get_public_key().as_hex(), nonce=hex(random.randint(0, 2**64)) ).SerializeToString() signature = self._signer.sign(header) transaction = Transaction( header=header, payload=payload, header_signature=signature ) batch_list = self._create_batch_list([transaction]) batch_id = batch_list.batches[0].header_signature if self._wait and self._wait > 0: wait_time = 0 start_time = time.time() response = self._send_request( "batches", batch_list.SerializeToString(), 'application/octet-stream' ) while wait_time < self._wait: status = self._get_status( batch_id, self._wait - int(wait_time) ) wait_time = time.time() - start_time if status != 'PENDING': return response return response return self._send_request( "batches", batch_list.SerializeToString(), 'application/octet-stream') def create(self, name, ships): """ Create battleship game """ update = { 'Action': 'CREATE', 'Name': name, 'Ships': ships } return self._send_battleship_txn(update) def join(self, name, board): """ User joins battleship game """ update = { 'Action': 'JOIN', 'Name': name, 'Board': board } return self._send_battleship_txn(update) def fire(self, name, column, row, reveal_space, reveal_nonce): """ Fire at (column, row) """ update = { 'Action': 'FIRE', 'Name': name, 'Column': column, 'Row': row } if reveal_space is not None: update['RevealSpace'] = reveal_space if reveal_nonce is not None: update['RevealNonce'] = reveal_nonce return self._send_battleship_txn(update) def list_games(self, auth_user=None, auth_password=None): prefix = self._get_prefix() result = self._send_request( "state?address={}".format(prefix), auth_user=auth_user, auth_password=auth_password ) try: encoded_entries = yaml.safe_load(result)["data"] ret = {} for entry in encoded_entries: d = json.loads(b64decode(entry["data"]).decode()) for k, v in d.items(): ret[k] = v return ret except BaseException: return None def _sha512(self, data): return hashlib.sha512(data).hexdigest() def _get_prefix(self): return self._sha512(self._transaction_family.encode('utf-8'))[0:6] def _get_address(self, name): prefix = self._get_prefix() game_address = self._sha512(name.encode('utf-8'))[0:64] return prefix + game_address def _create_batch_list(self, transactions): transaction_signatures = [t.header_signature for t in transactions] header = BatchHeader( signer_public_key=self._signer.get_public_key().as_hex(), transaction_ids=transaction_signatures ).SerializeToString() signature = self._signer.sign(header) batch = Batch( header=header, transactions=transactions, header_signature=signature ) return BatchList(batches=[batch]) def _get_status(self, batch_id, wait): try: result = self._send_request( 'batch_statuses?id={}&wait={}'.format(batch_id, wait)) return yaml.safe_load(result)['data'][0]['status'] except BaseException as err: raise BattleshipException(err) def _send_request( self, suffix, data=None, content_type=None, name=None, auth_user=None, auth_password=None): if self._base_url.startswith("http://"): url = "{}/{}".format(self._base_url, suffix) else: url = "http://{}/{}".format(self._base_url, suffix) headers = {} if auth_user is not None: auth_string = "{}:{}".format(auth_user, auth_password) b64_string = b64encode(auth_string.encode()).decode() auth_header = 'Basic {}'.format(b64_string) headers['Authorization'] = auth_header if content_type is not None: headers['Content-Type'] = content_type try: if data is not None: result = requests.post(url, headers=headers, data=data) else: result = requests.get(url, headers=headers) if result.status_code == 404: raise BattleshipException("No such game: {}".format(name)) elif not result.ok: raise BattleshipException("Error {}: {}".format( result.status_code, result.reason)) except BaseException as err: raise BattleshipException(err) return result.text
def setUp(self): self._temp_dir = tempfile.mkdtemp() context = create_context('secp256k1') private_key = context.new_random_private_key() crypto_factory = CryptoFactory(context) self._signer = crypto_factory.new_signer(private_key)
class IntkeyClient: def __init__(self, url, keyfile=None): self.url = url if keyfile is not None: try: with open(keyfile) as fd: private_key_str = fd.read().strip() fd.close() except OSError as err: raise IntkeyClientException( 'Failed to read private key: {}'.format(str(err))) try: private_key = Secp256k1PrivateKey.from_hex(private_key_str) except ParseError as e: raise IntkeyClientException( 'Unable to load private key: {}'.format(str(e))) self._signer = CryptoFactory( create_context('secp256k1')).new_signer(private_key) def set(self, name, value, wait=None): return self._send_transaction('set', name, value, wait=wait) def inc(self, name, value, wait=None): return self._send_transaction('inc', name, value, wait=wait) def dec(self, name, value, wait=None): return self._send_transaction('dec', name, value, wait=wait) def list(self): result = self._send_request( "state?address={}".format( self._get_prefix())) try: encoded_entries = yaml.safe_load(result)["data"] return [ cbor.loads(base64.b64decode(entry["data"])) for entry in encoded_entries ] except BaseException: return None def show(self, name): address = self._get_address(name) result = self._send_request("state/{}".format(address), name=name,) try: return cbor.loads( base64.b64decode( yaml.safe_load(result)["data"]))[name] except BaseException: return None def _get_status(self, batch_id, wait): try: result = self._send_request( 'batch_statuses?id={}&wait={}'.format(batch_id, wait),) return yaml.safe_load(result)['data'][0]['status'] except BaseException as err: raise IntkeyClientException(err) def _get_prefix(self): return _sha512('intkey'.encode('utf-8'))[0:6] def _get_address(self, name): prefix = self._get_prefix() game_address = _sha512(name.encode('utf-8'))[64:] return prefix + game_address def _send_request(self, suffix, data=None, content_type=None, name=None): if self.url.startswith("http://"): url = "{}/{}".format(self.url, suffix) else: url = "http://{}/{}".format(self.url, suffix) headers = {} if content_type is not None: headers['Content-Type'] = content_type try: if data is not None: result = requests.post(url, headers=headers, data=data) else: result = requests.get(url, headers=headers) if result.status_code == 404: raise IntkeyClientException("No such key: {}".format(name)) elif not result.ok: raise IntkeyClientException("Error {}: {}".format( result.status_code, result.reason)) except requests.ConnectionError as err: raise IntkeyClientException( 'Failed to connect to REST API: {}'.format(err)) except BaseException as err: raise IntkeyClientException(err) return result.text def _send_transaction(self, verb, name, value, wait=None): payload = cbor.dumps({ 'Verb': verb, 'Name': name, 'Value': value, }) # Construct the address address = self._get_address(name) header = TransactionHeader( signer_public_key=self._signer.get_public_key().as_hex(), family_name="intkey", family_version="1.0", inputs=[address], outputs=[address], dependencies=[], payload_sha512=_sha512(payload), batcher_public_key=self._signer.get_public_key().as_hex(), nonce=hex(random.randint(0, 2**64)) ).SerializeToString() signature = self._signer.sign(header) transaction = Transaction( header=header, payload=payload, header_signature=signature ) batch_list = self._create_batch_list([transaction]) batch_id = batch_list.batches[0].header_signature if wait and wait > 0: wait_time = 0 start_time = time.time() response = self._send_request( "batches", batch_list.SerializeToString(), 'application/octet-stream', ) while wait_time < wait: status = self._get_status( batch_id, wait - int(wait_time), ) wait_time = time.time() - start_time if status != 'PENDING': return response return response return self._send_request( "batches", batch_list.SerializeToString(), 'application/octet-stream', ) def _create_batch_list(self, transactions): transaction_signatures = [t.header_signature for t in transactions] header = BatchHeader( signer_public_key=self._signer.get_public_key().as_hex(), transaction_ids=transaction_signatures ).SerializeToString() signature = self._signer.sign(header) batch = Batch( header=header, transactions=transactions, header_signature=signature) return BatchList(batches=[batch])
class XoClient: def __init__(self, base_url, keyfile=None): self._base_url = base_url if keyfile is None: self._signer = None return try: with open(keyfile) as fd: private_key_str = fd.read().strip() except OSError as err: raise XoException('Failed to read private key {}: {}'.format( keyfile, str(err))) try: private_key = Secp256k1PrivateKey.from_hex(private_key_str) except ParseError as e: raise XoException('Unable to load private key: {}'.format(str(e))) self._signer = CryptoFactory(create_context('secp256k1')) \ .new_signer(private_key) def create(self, name, wait=None, auth_user=None, auth_password=None): return self._send_xo_txn(name, "create", wait=wait, auth_user=auth_user, auth_password=auth_password) def delete(self, name, wait=None, auth_user=None, auth_password=None): return self._send_xo_txn(name, "delete", wait=wait, auth_user=auth_user, auth_password=auth_password) def take(self, name, space, wait=None, auth_user=None, auth_password=None): return self._send_xo_txn(name, "take", space, wait=wait, auth_user=auth_user, auth_password=auth_password) def list(self, auth_user=None, auth_password=None): xo_prefix = self._get_prefix() result = self._send_request("state?address={}".format(xo_prefix), auth_user=auth_user, auth_password=auth_password) try: encoded_entries = yaml.safe_load(result)["data"] return [ base64.b64decode(entry["data"]) for entry in encoded_entries ] except BaseException: return None def show(self, name, auth_user=None, auth_password=None): address = self._get_address(name) result = self._send_request("state/{}".format(address), name=name, auth_user=auth_user, auth_password=auth_password) try: return base64.b64decode(yaml.safe_load(result)["data"]) except BaseException: return None def _get_status(self, batch_id, wait, auth_user=None, auth_password=None): try: result = self._send_request('batch_statuses?id={}&wait={}'.format( batch_id, wait), auth_user=auth_user, auth_password=auth_password) return yaml.safe_load(result)['data'][0]['status'] except BaseException as err: raise XoException(err) def _get_prefix(self): return _sha512('xo'.encode('utf-8'))[0:6] def _get_address(self, name): xo_prefix = self._get_prefix() game_address = _sha512(name.encode('utf-8'))[0:64] return xo_prefix + game_address def _send_request(self, suffix, data=None, content_type=None, name=None, auth_user=None, auth_password=None): if self._base_url.startswith("http://"): url = "{}/{}".format(self._base_url, suffix) else: url = "http://{}/{}".format(self._base_url, suffix) headers = {} if auth_user is not None: auth_string = "{}:{}".format(auth_user, auth_password) b64_string = b64encode(auth_string.encode()).decode() auth_header = 'Basic {}'.format(b64_string) headers['Authorization'] = auth_header if content_type is not None: headers['Content-Type'] = content_type try: if data is not None: result = requests.post(url, headers=headers, data=data) else: result = requests.get(url, headers=headers) if result.status_code == 404: raise XoException("No such game: {}".format(name)) if not result.ok: raise XoException("Error {}: {}".format( result.status_code, result.reason)) except requests.ConnectionError as err: raise XoException('Failed to connect to {}: {}'.format( url, str(err))) except BaseException as err: raise XoException(err) return result.text def _send_xo_txn(self, name, action, space="", wait=None, auth_user=None, auth_password=None): # Serialization is just a delimited utf-8 encoded string payload = ",".join([name, action, str(space)]).encode() # Construct the address address = self._get_address(name) header = TransactionHeader( signer_public_key=self._signer.get_public_key().as_hex(), family_name="xo", family_version="1.0", inputs=[address], outputs=[address], dependencies=[], payload_sha512=_sha512(payload), batcher_public_key=self._signer.get_public_key().as_hex(), nonce=hex(random.randint(0, 2**64))).SerializeToString() signature = self._signer.sign(header) transaction = Transaction(header=header, payload=payload, header_signature=signature) batch_list = self._create_batch_list([transaction]) batch_id = batch_list.batches[0].header_signature if wait and wait > 0: wait_time = 0 start_time = time.time() response = self._send_request("batches", batch_list.SerializeToString(), 'application/octet-stream', auth_user=auth_user, auth_password=auth_password) while wait_time < wait: status = self._get_status(batch_id, wait - int(wait_time), auth_user=auth_user, auth_password=auth_password) wait_time = time.time() - start_time if status != 'PENDING': return response return response return self._send_request("batches", batch_list.SerializeToString(), 'application/octet-stream', auth_user=auth_user, auth_password=auth_password) def _create_batch_list(self, transactions): transaction_signatures = [t.header_signature for t in transactions] header = BatchHeader( signer_public_key=self._signer.get_public_key().as_hex(), transaction_ids=transaction_signatures).SerializeToString() signature = self._signer.sign(header) batch = Batch(header=header, transactions=transactions, header_signature=signature) return BatchList(batches=[batch])
BIDS_TABLE = FAMILY_NAME + BIDDING_ENTRY_TABLE + BIDS_TABLE[:58] ITEMS_TABLE = FAMILY_NAME + BIDDING_ENTRY_TABLE + ITEMS_TABLE[:58] base_url = "http://rest-api:8008" import optparse import argparse FAMILY_NAME = hashlib.sha256("bidding".encode()).hexdigest()[:6] parser = optparse.OptionParser() parser.add_option('-U', '--url', action="store", dest="url", default="spam") namespace = "" context = create_context('secp256k1') private_key = context.new_random_private_key() signer = CryptoFactory(context).new_signer(private_key) public_key = signer.get_public_key().as_hex() def hash(name, n=0): if (n == 0): return hashlib.sha512(name.encode()).hexdigest() else: return hashlib.sha512(name.encode()).hexdigest()[:n] def showitems(): result = send_to_rest_api("state/{}".format(ITEMS_TABLE)) try:
class TransferClient: def __init__(self, base_url , keyfile = None): self._base_url = base_url if keyfile is None: self._signer = None return try: with open(keyfile) as f: private_key_str = f.read().strip() except OSError as err: raise TransferException('Failed to read private key {} : {}'.format(keyfile , str(err))) try: private_key = Secp256k1PrivateKey.from_hex(private_key_str) except ParseError as e: raise TransferException('Failed to load private key : {}'.format(str(err))) self._signer = CryptoFactory(create_context('secp256k1')).new_signer(private_key) def createBox(self, medicineName, medicineID, units, wait = None, auth_user = None, auth_password = None): boxID = ''.join(random.sample('0123456789', 5)) return self._send_transfer_txn( medicineName, medicineID, units, '', '', boxID, self._signer.get_public_key().as_hex(), '', '', '', 'createBox', wait, auth_user, auth_password ) def updateBox(self, medicineName, medicineID, units, boxID, wait = None, auth_user = None, auth_password = None): return self._send_transfer_txn( medicineName, medicineID, units, '', '', boxID, self._signer.get_public_key().as_hex(), '', '', '', 'updateBox', wait, auth_user, auth_password ) def createShipment(self, boxIDArray, origin, destination, wait = None, auth_user = None, auth_password = None): shipmentID = ''.join(random.sample('0123456789', 5)) shipmentStatus = 'CREATED' return self._send_transfer_txn( '', '', '', origin, destination, '', self._signer.get_public_key().as_hex(), shipmentID, boxIDArray, shipmentStatus, 'createShipment', wait, auth_user, auth_password ) def updateShipmentStatus(self, shipmentID, shipmentStatus, wait = None, auth_user = None, auth_password = None): return self._send_transfer_txn( '', '', '', '', '', '', self._signer.get_public_key().as_hex(), shipmentID, '', shipmentStatus, 'updateShipmentStatus', wait, auth_user, auth_password ) def deleteShipment(self, shipmentID, wait = None, auth_user = None, auth_password = None): return self._send_transfer_txn( '', '', '', '', '', '', self._signer.get_public_key().as_hex(), shipmentID, '', '', 'deleteShipment', wait, auth_user, auth_password ) def list(self, auth_user=None, auth_password=None): transfer_prefix = self._get_prefix() result = self._send_request( "state?address={}".format(transfer_prefix), auth_user=auth_user, auth_password=auth_password) try: encoded_entries = yaml.safe_load(result)["data"] return [ base64.b64decode(entry["data"]) for entry in encoded_entries ] except BaseException: return None def show(self, name, auth_user=None, auth_password=None): address = self._get_address(name) result = self._send_request( "state/{}".format(address), name=name, auth_user=auth_user, auth_password=auth_password) try: return base64.b64decode(yaml.safe_load(result)["data"]) except BaseException: return None def _get_status(self, batch_id, wait, auth_user=None, auth_password=None): try: result = self._send_request( 'batch_statuses?id={}&wait={}'.format(batch_id, wait), auth_user=auth_user, auth_password=auth_password) return yaml.safe_load(result)['data'][0]['status'] except BaseException as err: raise TransferException(err) def _get_prefix(self): return(_sha512('transfer'.encode('utf-8'))[0:6]) def _get_address(self, name): transfer_prefix = self._get_prefix() transfer_address = _sha512(name.encode('utf-8'))[0:64] return(transfer_prefix + transfer_address) def _send_request(self, suffix, data=None, content_type=None, name=None, auth_user=None, auth_password=None): if self._base_url.startswith("http://"): url = "{}/{}".format(self._base_url, suffix) else: url = "http://{}/{}".format(self._base_url, suffix) headers = {} if auth_user is not None: auth_string = "{}:{}".format(auth_user, auth_password) b64_string = b64encode(auth_string.encode()).decode() auth_header = 'Basic {}'.format(b64_string) headers['Authorization'] = auth_header if content_type is not None: headers['Content-Type'] = content_type try: if data is not None: result = requests.post(url, headers=headers, data=data) else: result = requests.get(url, headers=headers) if result.status_code == 404: raise TransferException("No such transfers: {}".format(name)) if not result.ok: raise TransferException("Error {}: {}".format(result.status_code, result.reason)) except requests.ConnectionError as err: raise TransferException( 'Failed to connect to {}: {}'.format(url, str(err))) except BaseException as err: raise TransferException(err) return(result.text) def _send_transfer_txn(self, medicineName, medicineID, units, origin, destination, boxID, logisticsID, shipmentID, boxIDArray, shipmentStatus, action, wait = None, auth_user = None, auth_password =None): payload = ",".join([medicineName, str(medicineID), str(units), origin, destination, str(boxID), str(logisticsID), str(shipmentID), str(boxIDArray), shipmentStatus, action]).encode() addresss = self._get_address(str(shipmentID)) addressb = self._get_address(str(boxID)) if(boxID is None): address = self._get_address(str(shipmentID)) else: address = self._get_address(str(boxID)) med_address = _make_medicine_address(medicineName) header = TransactionHeader( signer_public_key=self._signer.get_public_key().as_hex(), family_name="transfer", family_version="1.0", inputs=[addresss, addressb, address, med_address], outputs=[addresss, addressb, address, med_address], dependencies=[], payload_sha512=_sha512(payload), batcher_public_key=self._signer.get_public_key().as_hex(), nonce=hex(random.randint(0, 2**64)) ).SerializeToString() signature = self._signer.sign(header) transaction = Transaction( header=header, payload=payload, header_signature=signature ) batch_list = self._create_batch_list([transaction]) batch_id = batch_list.batches[0].header_signature if wait and wait > 0: wait_time = 0 start_time = time.time() response = self._send_request( "batches", batch_list.SerializeToString(), 'application/octet-stream', auth_user=auth_user, auth_password=auth_password) while wait_time < wait: status = self._get_status( batch_id, wait - int(wait_time), auth_user=auth_user, auth_password=auth_password) wait_time = time.time() - start_time if status != 'PENDING': return response return response return self._send_request( "batches", batch_list.SerializeToString(), 'application/octet-stream', auth_user=auth_user, auth_password=auth_password) def _create_batch_list(self, transactions): transaction_signatures = [t.header_signature for t in transactions] header = BatchHeader( signer_public_key=self._signer.get_public_key().as_hex(), transaction_ids=transaction_signatures ).SerializeToString() signature = self._signer.sign(header) batch = Batch( header=header, transactions=transactions, header_signature=signature) return BatchList(batches=[batch])
class IoTClient: def __init__(self, url, keyfile=None): self.url = url if keyfile is not None: try: with open(keyfile) as fd: private_key_str = fd.read().strip() fd.close() except OSError as err: raise IoTException('Failed to read private key: {}'.format( str(err))) try: private_key = Secp256k1PrivateKey.from_hex(private_key_str) except ParseError as e: raise IoTException('Unable to load private key: {}'.format( str(e))) self._signer = CryptoFactory( create_context('secp256k1')).new_signer(private_key) def send(self, path): #get user public, we will use as ID device_id = self._signer.get_public_key().as_hex()[:4] #get image metadata metadata = _get_metadata(path) #get timestamp timestamp = _get_date() if metadata is None: return "Unable to open Image" return self._send_transaction(device_id, metadata, timestamp) def list(self): result = self._send_request("state?address={}".format( self._get_prefix())) try: encoded_entries = yaml.safe_load(result)["data"] return [ cbor.loads(base64.b64decode(entry["data"])) for entry in encoded_entries ] except BaseException: return None def _get_prefix(self): return _sha512('iot-tp'.encode('utf-8'))[0:6] def _get_address(self, metadata): prefix = self._get_prefix() txn_address = _sha512(yaml.dump(metadata).encode('utf-8'))[64:] return prefix + txn_address def _send_request(self, suffix, data=None, content_type=None, name=None): if self.url.startswith("http://"): url = "{}/{}".format(self.url, suffix) else: url = "http://{}/{}".format(self.url, suffix) headers = {} if content_type is not None: headers['Content-Type'] = content_type try: if data is not None: result = requests.post(url, headers=headers, data=data) else: result = requests.get(url, headers=headers) if result.status_code == 4004: raise IoTClient("No such key: {}".format(name)) elif not result.ok: raise IoTClient("Error {}: {}".format(result.status_code, result.reason)) except requests.ConnectionError as err: raise IoTClient('Failed to connect to REST API: {}'.format(err)) except BaseException as err: raise IoTClient(err) return result.text def _send_transaction(self, device_id, metadata, timestamp): payload = cbor.dumps({ 'device_id': device_id, 'metadata': metadata, 'timestamp': timestamp, }) address = self._get_address(metadata) header = TransactionHeader( signer_public_key=self._signer.get_public_key().as_hex(), family_name="iot-tp", family_version="1.0", inputs=[address], outputs=[address], dependencies=[], payload_sha512=_sha512(payload), batcher_public_key=self._signer.get_public_key().as_hex(), nonce=hex(random.randint(0, 2**64))).SerializeToString() signature = self._signer.sign(header) transaction = Transaction(header=header, payload=payload, header_signature=signature) batch_list = self._create_batch_list([transaction]) batch_id = batch_list.batches[0].header_signature return self._send_request( "batches", batch_list.SerializeToString(), 'application/octet-stream', ) def _create_batch_list(self, transactions): transaction_signatures = [t.header_signature for t in transactions] header = BatchHeader( signer_public_key=self._signer.get_public_key().as_hex(), transaction_ids=transaction_signatures).SerializeToString() signature = self._signer.sign(header) batch = Batch(header=header, transactions=transactions, header_signature=signature) return BatchList(batches=[batch])
class BgtClient: def __init__(self, url, keyfile=None): self.url = url if keyfile is not None: try: with open(keyfile) as fd: private_key_str = fd.read().strip() fd.close() except OSError as err: raise BgtClientException( 'Failed to read private key: {}'.format(str(err))) try: private_key = Secp256k1PrivateKey.from_hex(private_key_str) except ParseError as e: raise BgtClientException( 'Unable to load private key: {}'.format(str(e))) self._signer = CryptoFactory( create_context('secp256k1')).new_signer(private_key) def set(self, name, value, wait=None): return self._send_transaction('set', name, value, to=None, wait=wait) def inc(self, name, value, wait=None): return self._send_transaction('inc', name, value, to=None, wait=wait) def dec(self, name, value, wait=None): return self._send_transaction('dec', name, value, to=None, wait=wait) def trans(self, name, value, to, wait=None): return self._send_transaction('trans', name, value, to=to, wait=wait) def list(self): result = self._send_request( "state?address={}".format( self._get_prefix())) try: encoded_entries = yaml.safe_load(result)["data"] return [ cbor.loads(base64.b64decode(entry["data"])) for entry in encoded_entries ] except BaseException: return None def show(self, name): address = self._get_address(name) result = self._send_request("state/{}".format(address), name=name,) try: return cbor.loads(base64.b64decode(yaml.safe_load(result)["data"]))[name] except BaseException: return None def _get_status(self, batch_id, wait): try: result = self._send_request( 'batch_statuses?id={}&wait={}'.format(batch_id, wait),) return yaml.safe_load(result)['data'][0]['status'] except BaseException as err: raise BgtClientException(err) def _get_prefix(self): return _sha512('bgt'.encode('utf-8'))[0:6] def _get_address(self, name): prefix = self._get_prefix() game_address = _sha512(name.encode('utf-8'))[64:] return prefix + game_address def _send_request(self, suffix, data=None, content_type=None, name=None): if self.url.startswith("http://"): url = "{}/{}".format(self.url, suffix) else: url = "http://{}/{}".format(self.url, suffix) headers = {} if content_type is not None: headers['Content-Type'] = content_type try: if data is not None: result = requests.post(url, headers=headers, data=data) else: result = requests.get(url, headers=headers) if result.status_code == 404: raise BgtClientException("No such key: {}".format(name)) elif not result.ok: raise BgtClientException("Error {}: {}".format( result.status_code, result.reason)) except requests.ConnectionError as err: raise BgtClientException( 'Failed to connect to REST API: {}'.format(err)) except BaseException as err: raise BgtClientException(err) return result.text def _send_transaction(self, verb, name, value, to=None, wait=None): val = { 'Verb': verb, 'Name': name, 'Value': value, } if to is not None: val['To'] = to payload = cbor.dumps(val) # Construct the address address = self._get_address(name) inputs = [address] outputs = [address] if to is not None: address_to = self._get_address(to) inputs.append(address_to) outputs.append(address_to) header = TransactionHeader( signer_public_key=self._signer.get_public_key().as_hex(), family_name=FAMILY_NAME, family_version=FAMILY_VERSION, inputs=inputs, outputs=outputs, dependencies=[], payload_sha512=_sha512(payload), batcher_public_key=self._signer.get_public_key().as_hex(), nonce=hex(random.randint(0, 2**64)) ).SerializeToString() signature = self._signer.sign(header) transaction = Transaction( header=header, payload=payload, header_signature=signature ) batch_list = self._create_batch_list([transaction]) batch_id = batch_list.batches[0].header_signature if wait and wait > 0: wait_time = 0 start_time = time.time() response = self._send_request( "batches", batch_list.SerializeToString(), 'application/octet-stream', ) while wait_time < wait: status = self._get_status( batch_id, wait - int(wait_time), ) wait_time = time.time() - start_time if status != 'PENDING': return response return response return self._send_request( "batches", batch_list.SerializeToString(), 'application/octet-stream', ) def _create_batch_list(self, transactions): transaction_signatures = [t.header_signature for t in transactions] header = BatchHeader( signer_public_key=self._signer.get_public_key().as_hex(), transaction_ids=transaction_signatures ).SerializeToString() signature = self._signer.sign(header) batch = Batch( header=header, transactions=transactions, header_signature=signature, timestamp=int(time.time())) return BatchList(batches=[batch])
class SchedulerTester(object): """ The canonical form of the yaml is: - <------------------------------------------ batch start state_hash: string. Optional. No default. - <----------------------------------------- transaction start inputs: list of string. Required. - .... outputs: list of string. Required. - .... addresses_to_set: list of dict. Optional. - string <address>: Optional bytes <value> addresses_to_delete: list of str. Optional - string <address> valid: boolean. Optional. Defaults to True dependencies: list of string. Optional. Defaults to empty list. - ..... string. No default. If a dependency is the same string as a 'name' for another txn, that txn's signature will be used for the actual Transaction's dependency. If the string is not an 'name' of another txn, if it is longer than 20 characters it will be used as if is is the actual Transaction.header_signature for the dependency. If not, it will be disregarded. name: string. Optional. No default. """ def __init__(self, file_name): """ Args: file_name (str): The yaml filename and path. scheduler (scheduler.Scheduler): Any Scheduler implementaion context_manager (context_manager.ContextManager): The context manager holding state for this scheduler. """ self._context = create_context('secp256k1') self._crypto_factory = CryptoFactory(self._context) self._yaml_file_name = file_name self._counter = itertools.count(0) self._referenced_txns_in_other_batches = {} self._batch_id_by_txn_id = {} self._txn_execution = {} self._batch_results = {} self._batches = [] self._create_batches() @property def batch_results(self): """The batch results calculated from the yaml file. Returns: (dict): Computed from the yaml file, a dictionary with batch signature keys and BatchExecutionResult values. """ return self._batch_results def run_scheduler(self, scheduler, context_manager, validation_state_hash=None, txns_executed_fifo=True): """Add all the batches to the scheduler in order and then run through the txns in the scheduler, calling next_transaction() after each transaction_execution_result is set. Args: scheduler (scheduler.Scheduler): Any implementation of the Scheduler abstract base class. context_manager (context_manager.ContextManager): The context manager is needed to store state based on the yaml file. validation_state_hash (str): Used in cases where the yaml represents a single block of valid batches, and the state hash is not in the yaml file. This state hash is added to the last batch in the scheduler. Returns batch_results (list of tuples): A list of tuples of batch signature, BatchExecutionResult pairs. """ for i, batch in enumerate(self._batches): if i == len(self._batches) - 1 and \ validation_state_hash is not None: s_h = validation_state_hash else: s_h = self._batch_results[batch.header_signature].state_hash scheduler.add_batch(batch=batch, state_hash=s_h) scheduler.finalize() txns_to_process = deque() txn_context_by_txn_id = self._compute_transaction_execution_context() transactions_to_assert_state = {} while not scheduler.complete(block=False): stop = False while not stop: try: txn_info = scheduler.next_transaction() except StopIteration: break if txn_info is not None: txns_to_process.append(txn_info) LOGGER.debug("Transaction %s scheduled", txn_info.txn.header_signature[:16]) else: stop = True try: if txns_executed_fifo: t_info = txns_to_process.popleft() else: t_info = txns_to_process.pop() except IndexError: # No new txn was returned from next_transaction so # check again if complete. continue inputs, outputs = self._get_inputs_outputs(t_info.txn) c_id = context_manager.create_context( state_hash=t_info.state_hash, base_contexts=t_info.base_context_ids, inputs=inputs, outputs=outputs) t_id = t_info.txn.header_signature if t_id in txn_context_by_txn_id: state_up_to_now = txn_context_by_txn_id[t_id].state txn_context = txn_context_by_txn_id[t_id] inputs, _ = self._get_inputs_outputs(txn_context.txn) addresses = [input for input in inputs if len(input) == 70] state_found = context_manager.get(context_id=c_id, address_list=addresses) LOGGER.debug( "Transaction Id %s, Batch %s, Txn %s, " "Context_id %s, Base Contexts %s", t_id[:16], txn_context.batch_num, txn_context.txn_num, c_id, t_info.base_context_ids) state_to_assert = [(add, state_up_to_now.get(add)) for add, _ in state_found] transactions_to_assert_state[t_id] = (txn_context, state_found, state_to_assert) validity, address_values, deletes = self._txn_execution[ t_info.txn.header_signature] context_manager.set(context_id=c_id, address_value_list=address_values) context_manager.delete(context_id=c_id, address_list=deletes) LOGGER.debug("Transaction %s is %s", t_id[:16], 'valid' if validity else 'invalid') scheduler.set_transaction_execution_result( txn_signature=t_info.txn.header_signature, is_valid=validity, context_id=c_id) batch_ids = [b.header_signature for b in self._batches] batch_results = [(b_id, scheduler.get_batch_execution_result(b_id)) for b_id in batch_ids] return batch_results, transactions_to_assert_state def run_scheduler_alternating(self, scheduler, context_manager, validation_state_hash=None, txns_executed_fifo=True): batches = deque() batches.extend(self._batches) txns_to_process = deque() txn_context_by_txn_id = self._compute_transaction_execution_context() transactions_to_assert_state = {} while not scheduler.complete(block=False): stop = False while not stop: try: txn_info = scheduler.next_transaction() except StopIteration: stop = True if txn_info is not None: txns_to_process.append(txn_info) LOGGER.debug("Transaction %s scheduled", txn_info.txn.header_signature[:16]) else: stop = True try: scheduler.add_batch(batches.popleft()) except IndexError: scheduler.finalize() try: if txns_executed_fifo: t_info = txns_to_process.popleft() else: t_info = txns_to_process.pop() except IndexError: # No new txn was returned from next_transaction so # check again if complete. continue inputs, outputs = self._get_inputs_outputs(t_info.txn) c_id = context_manager.create_context( state_hash=t_info.state_hash, base_contexts=t_info.base_context_ids, inputs=inputs, outputs=outputs) t_id = t_info.txn.header_signature if t_id in txn_context_by_txn_id: state_up_to_now = txn_context_by_txn_id[t_id].state txn_context = txn_context_by_txn_id[t_id] inputs, _ = self._get_inputs_outputs(txn_context.txn) addresses = [input for input in inputs if len(input) == 70] state_found = context_manager.get(context_id=c_id, address_list=addresses) LOGGER.debug( "Transaction Id %s, Batch %s, Txn %s, " "Context_id %s, Base Contexts %s", t_id[:16], txn_context.batch_num, txn_context.txn_num, c_id, t_info.base_context_ids) state_to_assert = [(add, state_up_to_now.get(add)) for add, _ in state_found] transactions_to_assert_state[t_id] = (txn_context, state_found, state_to_assert) validity, address_values, deletes = self._txn_execution[ t_info.txn.header_signature] context_manager.set(context_id=c_id, address_value_list=address_values) context_manager.delete(context_id=c_id, address_list=deletes) LOGGER.debug("Transaction %s is %s", t_id[:16], 'valid' if validity else 'invalid') scheduler.set_transaction_execution_result( txn_signature=t_info.txn.header_signature, is_valid=validity, context_id=c_id) batch_ids = [b.header_signature for b in self._batches] batch_results = [(b_id, scheduler.get_batch_execution_result(b_id)) for b_id in batch_ids] return batch_results, transactions_to_assert_state def compute_state_hashes_wo_scheduler(self, base_dir): """Creates a state hash from the state updates from each txn in a valid batch. Returns state_hashes (list of str): The merkle roots from state changes in 1 or more blocks in the yaml file. """ database = NativeLmdbDatabase(os.path.join( base_dir, 'compute_state_hashes_wo_scheduler.lmdb'), _size=10 * 1024 * 1024) tree = MerkleDatabase(database=database) state_hashes = [] updates = {} for batch in self._batches: b_id = batch.header_signature result = self._batch_results[b_id] if result.is_valid: for txn in batch.transactions: txn_id = txn.header_signature _, address_values, deletes = self._txn_execution[txn_id] batch_updates = {} for pair in address_values: batch_updates.update({a: pair[a] for a in pair.keys()}) # since this is entirely serial, any overwrite # of an address is expected and desirable. updates.update(batch_updates) for address in deletes: if address in updates: del updates[address] # This handles yaml files that have state roots in them if result.state_hash is not None: s_h = tree.update(set_items=updates, virtual=False) tree.set_merkle_root(merkle_root=s_h) state_hashes.append(s_h) if not state_hashes: state_hashes.append(tree.update(set_items=updates)) return state_hashes def _compute_transaction_execution_context(self): """Compute the serial state for each txn in the yaml file up to and including the invalid txn in each invalid batch. Notes: The TransactionExecutionContext for a txn will contain the state applied serially up to that point for each valid batch and then for invalid batches up to the invalid txn. Returns: dict: The transaction id to the TransactionExecutionContext """ transaction_contexts = {} state_up_to_now = {} for batch_num, batch in enumerate(self._batches): partial_batch_transaction_contexts = {} partial_batch_state_up_to_now = state_up_to_now.copy() for txn_num, txn in enumerate(batch.transactions): t_id = txn.header_signature is_valid, address_values, deletes = self._txn_execution[t_id] partial_batch_transaction_contexts[t_id] = \ TransactionExecutionContext( txn=txn, txn_num=txn_num + 1, batch_num=batch_num + 1, state=partial_batch_state_up_to_now.copy()) for item in address_values: partial_batch_state_up_to_now.update(item) for address in deletes: if address in partial_batch_state_up_to_now: partial_batch_state_up_to_now[address] = None if not is_valid: break batch_id = batch.header_signature batch_is_valid = self._batch_results[batch_id].is_valid if batch_is_valid: transaction_contexts.update(partial_batch_transaction_contexts) state_up_to_now.update(partial_batch_state_up_to_now) return transaction_contexts def _address(self, add, require_full=False): if ':sha' not in add and ',' not in add: return add if ',' in add: return binascii.hexlify(bytearray([int(i) for i in add.split(',')])) parts = add.split(':') if len(parts) == 3 and parts[2] == 'sha': # eg. 'yy:aaabbbb:sha' namespace = hashlib.sha512(parts[0].encode()).hexdigest()[:6] address = namespace + hashlib.sha512( parts[1].encode()).hexdigest()[:64] elif len(parts) == 3 and not require_full: # eg. 'a:sha:56' length = min(int(parts[2]), 70) address = hashlib.sha512(parts[0].encode()).hexdigest()[:length] elif len(parts) == 2: # eg. 'aaabbbb:sha' intermediate = parts[0] address = hashlib.sha512(intermediate.encode()).hexdigest()[:70] else: raise ValueError("Address specified by {} could " "not be formed".format(add)) return address def _get_inputs_outputs(self, txn): """Similarly to the TransactionExecutor, deserialize the inputs and outputs. Notes: The SchedulerTester has the inputs and outputs from the yaml file that it used to create the transaction, but it seems less error-prone to recreate the behavior of the TransactionExecutor. Args: txn (sawtooth_validator.protobuf.transaction_pb2.Transaction) Returns (tuple): (inputs, outputs) """ header = transaction_pb2.TransactionHeader() header.ParseFromString(txn.header) return list(header.inputs), list(header.outputs) def _bytes_if_none(self, value): if value is None: value = uuid.uuid4().hex.encode() return value def _yaml_from_file(self): with open(self._yaml_file_name, 'r') as infile: test_yaml = yaml.safe_load(infile) return test_yaml def _contains_and_not_none(self, key, obj): return key in obj and obj[key] is not None def _process_batches(self, yaml_batches, signer): batches = [] b_results = {} for batch in yaml_batches: batch_state_root = None if self._contains_and_not_none('state_hash', batch): batch_state_root = batch['state_hash'] txn_processing_result = self._process_txns( batch=batch, previous_batch_results=b_results.copy(), signer=signer) txns, batch_is_valid = txn_processing_result batch_real = create_batch(transactions=txns, signer=signer) for txn in txns: txn_id = txn.header_signature batch_id = batch_real.header_signature self._batch_id_by_txn_id[txn_id] = batch_id b_results[batch_real.header_signature] = BatchExecutionResult( is_valid=batch_is_valid, state_hash=batch_state_root) batches.append(batch_real) return batches, b_results def _dependencies_are_valid(self, dependencies, previous_batch_results): for dep in dependencies: if dep in self._batch_id_by_txn_id: batch_id = self._batch_id_by_txn_id[dep] dep_result = previous_batch_results[batch_id] if not dep_result.is_valid: return False return True def _process_txns(self, batch, previous_batch_results, signer): txns = [] referenced_txns = {} execution = {} batch_is_valid = True for transaction in batch: is_valid = True addresses_to_set = [] addresses_to_delete = [] inputs = transaction['inputs'] outputs = transaction['outputs'] inputs_real = [self._address(a) for a in inputs] outputs_real = [self._address(a) for a in outputs] if self._contains_and_not_none('addresses_to_set', transaction): addresses_to_set = [{ self._address(a, require_full=True): self._bytes_if_none(d[a]) for a in d } for d in transaction['addresses_to_set']] if self._contains_and_not_none('addresses_to_delete', transaction): addresses_to_delete = [ self._address(a, require_full=True) for a in transaction['addresses_to_delete'] ] if self._contains_and_not_none('dependencies', transaction): if any([ a not in self._referenced_txns_in_other_batches and len(a) <= 20 for a in transaction['dependencies'] ]): # This txn has a dependency with a txn signature that is # not known about, return None dependencies = [ self._referenced_txns_in_other_batches[a] if a in self._referenced_txns_in_other_batches else a for a in transaction['dependencies'] ] dependencies = [a for a in dependencies if len(a) > 20] else: dependencies = [] deps_valid = self._dependencies_are_valid(dependencies, previous_batch_results) if self._contains_and_not_none('valid', transaction): is_valid = bool(transaction['valid']) if not is_valid or not deps_valid: batch_is_valid = False txn, _ = create_transaction(payload=uuid.uuid4().hex.encode(), dependencies=dependencies, inputs=inputs_real, outputs=outputs_real, signer=signer) if self._contains_and_not_none('name', transaction): referenced_txns[transaction['name']] = txn.header_signature execution[txn.header_signature] = (is_valid, addresses_to_set, addresses_to_delete) txns.append(txn) self._txn_execution.update(execution) self._referenced_txns_in_other_batches.update(referenced_txns) return txns, batch_is_valid def _create_batches(self): test_yaml = self._yaml_from_file() private_key = self._context.new_random_private_key() signer = self._crypto_factory.new_signer(private_key) batches, batch_results = self._process_batches(yaml_batches=test_yaml, signer=signer) self._batch_results = batch_results self._batches = batches
def test_no_validator_registry( self, mock_utils, mock_validator_registry_view, mock_consensus_state, mock_poet_enclave_factory, mock_consensus_state_store, mock_poet_key_state_store, mock_signup_info, mock_poet_settings_view, mock_block_wrapper): """ Test verifies that PoET Block Publisher fails if a validator doesn't have any signup info in the validator registry (the validator is not listed in the validator registry) """ # create a mock_validator_registry_view that throws KeyError mock_validator_registry_view.return_value.get_validator_info. \ side_effect = KeyError('Non-existent validator') # create a mock_wait_certificate that does nothing in check_valid mock_wait_certificate = mock.Mock() mock_wait_certificate.check_valid.return_value = None mock_utils.deserialize_wait_certificate.return_value = \ mock_wait_certificate # create a mock_consensus_state that returns a mock with # the following settings: mock_state = MockConsensusState.create_mock_consensus_state() mock_consensus_state.consensus_state_for_block_id.return_value = \ mock_state mock_poet_key_state_store.return_value = \ _MockPoetKeyStateStore(active_key=None) # create mock_signup_info mock_signup_info.create_signup_info.return_value = \ mock.Mock( poet_public_key='poet public key', proof_data='proof data', anti_sybil_id='anti-sybil ID', sealed_signup_data='sealed signup data') mock_signup_info.block_id_to_nonce.return_value = 'nonce' # create mock_batch_publisher context = create_context('secp256k1') private_key = context.new_random_private_key() crypto_factory = CryptoFactory(context) signer = crypto_factory.new_signer(private_key) mock_batch_publisher = mock.Mock(identity_signer=signer) mock_block_cache = mock.MagicMock() mock_state_view_factory = mock.Mock() # create mock_block_header with the following fields mock_block = mock.Mock(identifier='0123456789abcdefedcba9876543210') mock_block.header.signer_public_key = \ '90834587139405781349807435098745' mock_block.header.previous_block_id = '2' mock_block.header.block_num = 1 mock_block.header.state_root_hash = '6' mock_block.header.batch_ids = '4' # check test block_publisher = \ poet_block_publisher.PoetBlockPublisher( block_cache=mock_block_cache, state_view_factory=mock_state_view_factory, batch_publisher=mock_batch_publisher, data_dir=self._temp_dir, config_dir=self._temp_dir, validator_id='validator_deadbeef') self.assertFalse( block_publisher.initialize_block(block_header=mock_block.header)) # check that batch publisher was called to send out # the txn header and txn for the validator registry update self.assertTrue(mock_batch_publisher.send.called)
class SawtoothClient(object): def __init__(self, url, keyfile=None): self.base_url = url if keyfile is not None: with open(keyfile) as fd: private_key_str = fd.read().strip() fd.close() sawtooth_signing_key = Secp256k1PrivateKey.from_hex( private_key_str) self._signer = CryptoFactory( create_context('secp256k1')).new_signer(sawtooth_signing_key) def add_net(self, network_id, payload, wait=None): address = make_network_address(network_id) return self._send_transaction(ActionTypes.ADD_NET.value, payload, [address], wait=wait) def _send_transaction(self, action, action_payload, addresses, wait=None): payload = cbor.dumps({'action': action, 'payload': action_payload}) header = TransactionHeader( signer_public_key=self._signer.get_public_key().as_hex(), family_name="billing-crdt", family_version="0.0.1", inputs=addresses, outputs=addresses, dependencies=[], payload_sha512=self._sha512(payload), batcher_public_key=self._signer.get_public_key().as_hex(), nonce=self._nonce()).SerializeToString() signature = self._signer.sign(header) transaction = Transaction(header=header, payload=payload, header_signature=signature) batch_list = self._create_batch_list([transaction]) batch_id = batch_list.batches[0].header_signature if wait and wait > 0: wait_time = 0 start_time = time.time() response = self._send_request( "batches", batch_list.SerializeToString(), 'application/octet-stream', ) while wait_time < wait: status = self._get_status( batch_id, wait - int(wait_time), ) wait_time = time.time() - start_time if status != 'PENDING': return response return response return self._send_request( "batches", batch_list.SerializeToString(), 'application/octet-stream', ) def _send_request(self, suffix, data=None, content_type=None, name=None): url = "{}/{}".format(self.base_url, suffix) headers = {} if content_type is not None: headers['Content-Type'] = content_type try: if data is not None: result = requests.post(url, headers=headers, data=data) else: result = requests.get(url, headers=headers) if not result.ok: raise CrdtClientException( "Error url={} name={} - {}: {}".format( suffix, name, result.status_code, result.reason)) except requests.ConnectionError as err: raise CrdtClientException( 'Failed to connect to REST API: {}'.format(err)) except BaseException as err: raise CrdtClientException(err) return result.text def _create_batch_list(self, transactions): transaction_signatures = [t.header_signature for t in transactions] header = BatchHeader( signer_public_key=self._signer.get_public_key().as_hex(), transaction_ids=transaction_signatures).SerializeToString() signature = self._signer.sign(header) batch = Batch(header=header, transactions=transactions, header_signature=signature) return BatchList(batches=[batch]) @staticmethod def _sha512(data): return hashlib.sha512(data).hexdigest() @staticmethod def _nonce(): return time.time().hex().encode()
class CarLoggerClient(object): '''Client car logger class. This supports create, add, delete, history functions. ''' def __init__(self, baseUrl, private_key=None, vin=''): '''Initialize the client class. This is mainly getting the key pair and computing the address. ''' self._baseUrl = baseUrl try: privateKey = Secp256k1PrivateKey.from_hex(private_key) except ParseError as err: raise Exception('Failed to load private key: {}'.format(str(err))) self._signer = CryptoFactory(create_context('secp256k1')) \ .new_signer(privateKey) self._publicKey = self._signer.get_public_key().as_hex() self.VIN = vin self._address = _hash(FAMILY_NAME.encode('utf-8'))[0:6] + \ _hash(self.VIN.encode('utf-8'))[0:64] # For each valid cli command in _cli.py file, # add methods to: # 1. Do any additional handling, if required # 2. Create a transaction and a batch # 2. Send to rest-api def create(self, VIN, keyfile, work_date, brand, model, description): return self._wrap_and_send("create", VIN, keyfile, work_date, brand, model, description) def add(self, VIN, keyfile, work_date, work, km_status, description): return self._wrap_and_send("add", VIN, keyfile, work_date, work, km_status, description) def delete(self, VIN, keyfile, work_date, work, km_status, description): return self._wrap_and_send("delete", VIN, keyfile, work_date, work, km_status, description) def history(self): result = self._send_to_restapi("state/{}".format(self._address)) try: return base64.b64decode(yaml.safe_load(result)["data"]) except BaseException: return None def _send_to_restapi(self, suffix, data=None, contentType=None): '''Send a REST command to the Validator via the REST API.''' if self._baseUrl.startswith("http://"): url = "{}/{}".format(self._baseUrl, suffix) else: url = "http://{}/{}".format(self._baseUrl, suffix) headers = {} if contentType is not None: headers['Content-Type'] = contentType try: if data is not None: result = requests.post(url, headers=headers, data=data) else: result = requests.get(url, headers=headers) if not result.ok: raise Exception("Error {}: {}".format(result.status_code, result.reason)) except requests.ConnectionError as err: raise Exception('Failed to connect to {}: {}'.format( url, str(err))) except BaseException as err: raise Exception(err) return result.text def _wrap_and_send(self, action, *values): '''Create a transaction, then wrap it in a batch. Even single transactions must be wrapped into a batch. ''' # Generate a csv utf-8 encoded string as payload rawPayload = action for val in values: rawPayload = ",".join([rawPayload, str(val)]) payload = rawPayload.encode() # Construct the address where we'll store our state address = self._address inputAddressList = [address] outputAddressList = [address] # Create a TransactionHeader header = TransactionHeader( signer_public_key=self._publicKey, family_name=FAMILY_NAME, family_version="1.0", inputs=inputAddressList, outputs=outputAddressList, dependencies=[], payload_sha512=_hash(payload), batcher_public_key=self._publicKey, nonce=random.random().hex().encode()).SerializeToString() # Create a Transaction from the header and payload above transaction = Transaction(header=header, payload=payload, header_signature=self._signer.sign(header)) transactionList = [transaction] # Create a BatchHeader from transactionList above header = BatchHeader( signer_public_key=self._publicKey, transaction_ids=[txn.header_signature for txn in transactionList]).SerializeToString() # Create Batch using the BatchHeader and transactionList above batch = Batch(header=header, transactions=transactionList, header_signature=self._signer.sign(header)) # Create a Batch List from Batch above batch_list = BatchList(batches=[batch]) # Send batch_list to rest-api return self._send_to_restapi("batches", batch_list.SerializeToString(), 'application/octet-stream')
def test_block_publisher_doesnt_claim_readiness( self, mock_utils, mock_validator_registry_view, mock_consensus_state, mock_poet_enclave_factory, mock_consensus_state_store, mock_poet_key_state_store, mock_signup_info, mock_wait_time, mock_poet_settings_view, mock_block_wrapper): """ Test verifies that PoET Block Publisher doesn't claims readiness if the wait timer hasn't expired """ # create a mock_validator_registry_view with # get_validator_info that does nothing mock_validator_registry_view.return_value.get_validator_info. \ return_value = \ ValidatorInfo( name='validator_001', id='validator_deadbeef', signup_info=SignUpInfo( poet_public_key='00112233445566778899aabbccddeeff')) # create a mock_consensus_state that returns a mock with # the following settings: mock_state = MockConsensusState.create_mock_consensus_state() mock_consensus_state.consensus_state_for_block_id.return_value = \ mock_state mock_consensus_state_store.return_value.__getitem__.return_value = \ mock_consensus_state # Create mock key state mock_poet_key_state_store.return_value.__getitem__.return_value = \ mock.Mock( sealed_signup_data='sealed signup data', has_been_refreshed=False) # create mock_signup_info mock_signup_info.unseal_signup_data.return_value = \ '00112233445566778899aabbccddeeff' # create mock_batch_publisher context = create_context('secp256k1') private_key = context.new_random_private_key() crypto_factory = CryptoFactory(context) signer = crypto_factory.new_signer(private_key) mock_batch_publisher = mock.Mock(identity_signer=signer) mock_block_cache = mock.MagicMock() mock_state_view_factory = mock.Mock() # create mock_block_header with the following fields mock_block = mock.Mock(identifier='0123456789abcdefedcba9876543210') mock_block.header.signer_public_key = \ '90834587139405781349807435098745' mock_block.header.previous_block_id = '2' mock_block.header.block_num = 1 mock_block.header.state_root_hash = '6' mock_block.header.batch_ids = '4' # create a mock_wait_timer that hasn't expired yet my_wait_time = mock.Mock() my_wait_time.has_expired.return_value = False mock_wait_time.create_wait_timer.return_value = my_wait_time # create mock_poet_enclave_module mock_poet_enclave_module = mock.Mock() mock_poet_enclave_module.return_value = \ mock_poet_enclave_factory.get_poet_enclave_module.return_value # check test block_publisher = \ poet_block_publisher.PoetBlockPublisher( block_cache=mock_block_cache, state_view_factory=mock_state_view_factory, batch_publisher=mock_batch_publisher, data_dir=self._temp_dir, config_dir=self._temp_dir, validator_id='validator_deadbeef') # check initialize_block() first to set wait_timer self.assertTrue( block_publisher.initialize_block(block_header=mock_block.header)) # check that block_publisher only claims readiness # when the wait_timer has expired self.assertFalse( block_publisher.check_publish_block( block_header=mock_block.header))
def test_z_policy(self, mock_utils, mock_validator_registry_view, mock_consensus_state, mock_poet_enclave_factory, mock_consensus_state_store, mock_poet_key_state_store, mock_signup_info, mock_poet_settings_view, mock_block_wrapper): """ Z Policy: Test verifies that PoET Block Publisher fails if a validator attempts to claim more blocks frequently than is allowed """ # create a mock_validator_registry_view with # get_validator_info that does nothing mock_validator_registry_view.return_value.get_validator_info. \ return_value = \ ValidatorInfo( name='validator_001', id='validator_deadbeef', signup_info=SignUpInfo( poet_public_key='00112233445566778899aabbccddeeff')) # create a mock_wait_certificate that does nothing in check_valid mock_wait_certificate = mock.Mock() mock_wait_certificate.check_valid.return_value = None mock_utils.deserialize_wait_certificate.return_value = \ mock_wait_certificate # create a mock_consensus_state that returns a mock with # the following settings: mock_state = MockConsensusState.create_mock_consensus_state( claiming_too_frequently=True) mock_consensus_state.consensus_state_for_block_id.return_value = \ mock_state mock_consensus_state_store.return_value.__getitem__.return_value = \ mock_consensus_state # Create mock key state mock_poet_key_state_store.return_value.__getitem__.return_value = \ mock.Mock( sealed_signup_data='sealed signup data', has_been_refreshed=False) # create mock_signup_info mock_signup_info.unseal_signup_data.return_value = \ '00112233445566778899aabbccddeeff' # create mock_batch_publisher context = create_context('secp256k1') private_key = context.new_random_private_key() crypto_factory = CryptoFactory(context) signer = crypto_factory.new_signer(private_key) mock_batch_publisher = mock.Mock(identity_signer=signer) mock_block_cache = mock.MagicMock() mock_state_view_factory = mock.Mock() # create mock_block_header with the following fields mock_block = mock.Mock(identifier='0123456789abcdefedcba9876543210') mock_block.header.signer_public_key = \ '90834587139405781349807435098745' mock_block.header.previous_block_id = '2' mock_block.header.block_num = 1 mock_block.header.state_root_hash = '6' mock_block.header.batch_ids = '4' # check test with mock.patch('sawtooth_poet.poet_consensus.poet_block_publisher.' 'LOGGER') as mock_logger: block_publisher = \ poet_block_publisher.PoetBlockPublisher( block_cache=mock_block_cache, state_view_factory=mock_state_view_factory, batch_publisher=mock_batch_publisher, data_dir=self._temp_dir, config_dir=self._temp_dir, validator_id='validator_deadbeef') self.assertFalse( block_publisher.initialize_block( block_header=mock_block.header)) # Could be a hack, but verify that the appropriate log message is # generated - so we at least have some faith that the failure was # because of what we are testing and not something else. I know # that this is fragile if the log message is changed, so would # accept any suggestions on a better way to verify that the # function fails for the reason we expect. (message, *_), _ = mock_logger.info.call_args self.assertTrue('is claiming blocks too ' 'frequently' in message)
class CapBACClient: def __init__(self, url, keyfile=None): self.url = url if keyfile is not None: try: with open(keyfile) as fd: private_key_str = fd.read().strip() fd.close() except OSError as err: raise CapBACClientException( 'Failed to read private key: {}'.format(str(err))) try: private_key = Secp256k1PrivateKey.from_hex(private_key_str) except ParseError as e: raise CapBACClientException( 'Unable to load private key: {}'.format(str(e))) self._signer = CryptoFactory( create_context('secp256k1')).new_signer(private_key) # For each valid cli commands in _cli.py file # Add methods to: # 1. Do any additional handling, if required # 2. Create a transaction and a batch # 2. Send to rest-api def issue(self, token, is_root): try: token = json.loads(token) except: raise CapBACClientException('Invalid token: serialization failed') return self.issue_from_dict(token, is_root) def issue_from_dict(self,token, is_root): # check the formal validity of the incomplete token subset = set(CAPABILITY_FORMAT) - {'II','SI','VR'} if is_root: subset -= {'IC','SU'} _check_format(token,'capabiliy token',CAPABILITY_FORMAT,subset) for access_right in token['AR']: _check_format(access_right,'capability token: access right',ACCESS_RIGHT_FORMAT) # time interval logical check try: not_before = int(token['NB']) not_after = int(token['NA']) except: raise CapBACClientException('Invalid capability: timestamp not a number') if not_before > not_after: raise CapBACClientException("Invalid capability: incorrect time interval") now = int(time.time()) if now > not_after: raise CapBACClientException("Capability already expired") if is_root: token['IC'] = None token['SU'] = self._signer.get_public_key().as_hex() # add signature token= self.sign_dict(token) # now the token is complete payload = cbor.dumps({ 'AC': "issue", 'OB': token }) return self._send_transaction(payload, token['DE']) def revoke(self, token): try: token = json.loads(token) except: raise CapBACClientException('Invalid revocation token: serialization failed') return self.revoke_from_dict(token) def revoke_from_dict(self, token): # check the formal validity of the incomplete revocation token subset = set(REVOCATION_FORMAT) - {'II','SI','VR'} _check_format(token,'revocation token',REVOCATION_FORMAT,subset) # add signature token = self.sign_dict(token) # now the revocation token is complete payload = cbor.dumps({ 'AC': "revoke", 'OB': token }) return self._send_transaction(payload, token['DE']) def list(self,device): if len(device) > MAX_URI_LENGTH: raise CapBACClientException( 'Invalid URI: max length exceeded, should be less than {}' .format(MAX_URI_LENGTH)) result = self._send_request( "state?address={}".format( self._get_address(device))) try: encoded_entries = yaml.safe_load(result)["data"] data_list = [ cbor.loads(base64.b64decode(entry["data"])) for entry in encoded_entries ] return json.dumps({ x:y[x] for y in data_list for x in y }, indent=4, sort_keys=True) except BaseException: return None def validate(self,token): try: token = json.loads(token) except: raise CapBACClientException('Invalid access token: serialization failed') return self.validate_from_dict(token) def validate_from_dict(self,token): _check_format(token,"access token",VALIDATION_FORMAT) # state retrival device = token['DE'] result = self._send_request( "state?address={}".format( self._get_address(device))) try: encoded_entries = yaml.safe_load(result)["data"] data_list = [ cbor.loads(base64.b64decode(entry["data"])) for entry in encoded_entries ] state = {x:y[x] for y in data_list for x in y} except BaseException: return None LOGGER.info('checking authorization') # check authorization capability = token['IC'] if capability not in state: return False LOGGER.info('checking delegation chain') # delegation chain check now = int(time.time()) resource = token['RE'] action = token['AC'] current_token = state[capability] parent = current_token['IC'] while parent != None: if parent not in state: raise BaseException parent_token = state[parent] # check time interval if now >= int(parent_token['NA']): return False if now < int(parent_token['NB']): return False # check access rights if resource not in parent_token["AR"]: return False if action not in parent_token["AR"][resource]: return False # next current_token = parent_token parent = current_token['IC'] LOGGER.info('checking signature') # check signature signature = token.pop('SI') if not create_context('secp256k1').verify( signature, str(cbor.dumps(token,sort_keys=True)).encode('utf-8'), Secp256k1PublicKey.from_hex(state[capability]['SU']) ): return False return True def sign(self, token): try: token = json.loads(token) except: raise CapBACClientException('Invalid token: serialization failed') token = self.sign_dict(token) return json.dumps(token) def sign_dict(self, token): # add version token['VR'] = FAMILY_VERSION # add issue time now = int(time.time()) token['II'] = str(now) # add signature token_serialized = str(cbor.dumps(token,sort_keys=True)).encode('utf-8') token['SI'] = self._signer.sign(token_serialized) return token def _get_prefix(self): return _sha512(FAMILY_NAME.encode('utf-8'))[0:6] def _get_address(self, device): prefix = self._get_prefix() device_address = _sha512(device.encode('utf-8'))[64:] return prefix + device_address def _send_request(self, suffix, data=None, contentType=None): if self.url.startswith("http://"): url = "{}/{}".format(self.url, suffix) else: url = "http://{}/{}".format(self.url, suffix) headers = {} if contentType is not None: headers['Content-Type'] = contentType try: if data is not None: result = requests.post(url, headers=headers, data=data) else: result = requests.get(url, headers=headers) if not result.ok: raise CapBACClientException("Error {}: {}".format( result.status_code, result.reason)) except requests.ConnectionError as err: raise CapBACClientException( 'Failed to connect to {}: {}'.format(url, str(err))) except BaseException as err: raise CapBACClientException(err) return result.text def _send_transaction(self, payload, device): # Get the unique address for the device's tokens address = self._get_address(device) header = TransactionHeader( signer_public_key=self._signer.get_public_key().as_hex(), family_name=FAMILY_NAME, family_version=FAMILY_VERSION, inputs=[address], outputs=[address], dependencies=[], payload_sha512=_sha512(payload), batcher_public_key=self._signer.get_public_key().as_hex(), nonce=time.time().hex().encode() ).SerializeToString() signature = self._signer.sign(header) transaction = Transaction( header=header, payload=payload, header_signature=signature ) batch_list = self._create_batch_list([transaction]) return self._send_request( "batches", batch_list.SerializeToString(), 'application/octet-stream' ) def _create_batch_list(self, transactions): transaction_signatures = [t.header_signature for t in transactions] header = BatchHeader( signer_public_key=self._signer.get_public_key().as_hex(), transaction_ids=transaction_signatures ).SerializeToString() signature = self._signer.sign(header) batch = Batch( header=header, transactions=transactions, header_signature=signature) return BatchList(batches=[batch])
class SawtoothHelper: def __init__(self, base_url, validator_url=None, pk=None, context=None): self.base_url = base_url self.validator_url = validator_url if validator_url else f"{base_url}:4004" self.context = context if context else create_context("secp256k1") self.pk = pk if pk else self.context.new_random_private_key() self.signer = CryptoFactory(self.context).new_signer(self.pk) self.family_name = "DECODE_PETITION" self.family_version = "1.0" @property def private_key(self): return self.pk.secp256k1_private_key.serialize() def set_url(self, url): self.base_url = url def create_transaction(self, payload, family_name, family_version, address): payload_bytes = cbor2.dumps(payload) txn_header = TransactionHeader( batcher_public_key=self.signer.get_public_key().as_hex(), inputs=[address], outputs=[address], dependencies=[], family_name=family_name, family_version=family_version, nonce=hex(randint(0, 2**64)), payload_sha512=sha512(payload_bytes).hexdigest(), signer_public_key=self.signer.get_public_key().as_hex(), ).SerializeToString() txn = Transaction( header=txn_header, header_signature=self.signer.sign(txn_header), payload=payload_bytes, ) return [txn] def create_batch(self, transactions): batch_header = BatchHeader( signer_public_key=self.signer.get_public_key().as_hex(), transaction_ids=[txn.header_signature for txn in transactions], ).SerializeToString() batch = Batch( header=batch_header, header_signature=self.signer.sign(batch_header), transactions=transactions, ) return BatchList(batches=[batch]).SerializeToString() def get_state(self, payload, address): petition_address = self.generate_address(self.family_name, payload) r = requests.get(f"{address}/state?address={petition_address}") return r.json() def get_batches(self, payload, address): state = self.get_state(payload, address) r = requests.get(f"{address}/blocks/{state['head']}") batches = r.json()["data"]["batches"] return batches def _post(self, payload, family_name, family_version, address): transactions = self.create_transaction(payload, family_name, family_version, address) batches = self.create_batch(transactions) response = requests.post( f"{self.base_url}", data=batches, headers={"Content-Type": "application/octet-stream"}, ) return response.json() def post(self, payload): address = self.generate_address(self.family_name, payload) return self._post(payload, self.family_name, self.family_version, address) @staticmethod def generate_address(family_name, payload): namespace = sha512(family_name.encode("utf-8")).hexdigest()[0:6] petition = sha512( payload["petition_id"].encode("utf-8")).hexdigest()[-64:] return namespace + petition
def test_signup_info_not_committed_within_allowed_delay( self, mock_utils, mock_validator_registry_view, mock_consensus_state, mock_poet_enclave_factory, mock_consensus_state_store, mock_poet_key_state_store, mock_signup_info, mock_poet_settings_view, mock_block_wrapper): """ Test verifies that PoET Block Publisher fails if a validator's signup info was not committed to the block chain within the allowed configured delay """ # create a mock_validator_registry_view with # get_validator_info that does nothing mock_validator_registry_view.return_value.get_validator_info. \ return_value = \ ValidatorInfo( name='validator_001', id='validator_deadbeef', signup_info=SignUpInfo( poet_public_key='00112233445566778899aabbccddeeff', nonce='nonce')) # create a mock_wait_certificate that does nothing in check_valid mock_wait_certificate = mock.Mock() mock_wait_certificate.check_valid.return_value = None mock_utils.deserialize_wait_certificate.return_value = \ mock_wait_certificate # create a mock_consensus_state that returns a mock with # the following settings: mock_state = MockConsensusState.create_mock_consensus_state( committed_too_late=True) mock_consensus_state.consensus_state_for_block_id.return_value = \ mock_state mock_consensus_state_store.return_value.__getitem__.return_value = \ mock_consensus_state # Create mock key state mock_poet_key_state_store.return_value.__getitem__.return_value = \ mock.Mock( sealed_signup_data='sealed signup data', has_been_refreshed=False) # create mock_signup_info mock_signup_info.create_signup_info.return_value = \ mock.Mock( poet_public_key='poet public key', proof_data='proof data', anti_sybil_id='anti-sybil ID', sealed_signup_data='sealed signup data') mock_signup_info.block_id_to_nonce.return_value = 'nonce' mock_signup_info.unseal_signup_data.return_value = \ '00112233445566778899aabbccddeeff' # create mock_batch_publisher context = create_context('secp256k1') private_key = context.new_random_private_key() crypto_factory = CryptoFactory(context) signer = crypto_factory.new_signer(private_key) mock_batch_publisher = mock.Mock(identity_signer=signer) mock_block_cache = mock.MagicMock() mock_state_view_factory = mock.Mock() # create mock_block_header with the following fields mock_block = mock.Mock(identifier='0123456789abcdefedcba9876543210') mock_block.header.signer_public_key = \ '90834587139405781349807435098745' mock_block.header.previous_block_id = '2' mock_block.header.block_num = 1 mock_block.header.state_root_hash = '6' mock_block.header.batch_ids = '4' # check test with mock.patch('sawtooth_poet.poet_consensus.poet_block_publisher.' 'LOGGER') as mock_logger: block_publisher = \ poet_block_publisher.PoetBlockPublisher( block_cache=mock_block_cache, state_view_factory=mock_state_view_factory, batch_publisher=mock_batch_publisher, data_dir=self._temp_dir, config_dir=self._temp_dir, validator_id='validator_deadbeef') self.assertFalse( block_publisher.initialize_block( block_header=mock_block.header)) # Could be a hack, but verify that the appropriate log message is # generated - so we at least have some faith that the failure was # because of what we are testing and not something else. I know # that this is fragile if the log message is changed, so would # accept any suggestions on a better way to verify that the # function fails for the reason we expect. self.assertTrue( any( 'Validator signup information not committed in a timely ' 'manner.' in call[0][0] for call in mock_logger.info.call_args_list)) # check that create.signup_info() was called to create # the validator registry payload with new set of keys self.assertTrue(mock_signup_info.create_signup_info.called)
import os from sawtooth_signing import create_context from sawtooth_signing import CryptoFactory from sawtooth_signing.secp256k1 import Secp256k1PrivateKey if not os.path.exists('userskey'): context = create_context('secp256k1') with open('userskey', 'w') as f: for i in range(0, 100): private_key = context.new_random_private_key() print(private_key.as_hex()) f.write(private_key.as_hex() + '\n') with open('userskey', 'r') as f: signer_list = [line.rstrip('\n') for line in f.readlines()] print(signer_list) if not os.path.exists('users_pub_key'): context = create_context('secp256k1') with open('users_pub_key', 'w') as f: for key in signer_list: pub_key = CryptoFactory(context).new_signer( Secp256k1PrivateKey.from_hex(key)).get_public_key().as_hex() f.write(pub_key + ' ' + '1' + '\n')
def test_z_policy( self, mock_utils, mock_validator_registry_view, mock_consensus_state, mock_poet_enclave_factory, mock_consensus_state_store, mock_poet_key_state_store, mock_signup_info, mock_poet_settings_view, mock_block_wrapper): """ Z Policy: Test verifies that PoET Block Publisher fails if a validator attempts to claim more blocks frequently than is allowed """ # create a mock_validator_registry_view with # get_validator_info that does nothing mock_validator_registry_view.return_value.get_validator_info. \ return_value = \ ValidatorInfo( name='validator_001', id='validator_deadbeef', signup_info=SignUpInfo( poet_public_key='00112233445566778899aabbccddeeff')) # create a mock_wait_certificate that does nothing in check_valid mock_wait_certificate = mock.Mock() mock_wait_certificate.check_valid.return_value = None mock_utils.deserialize_wait_certificate.return_value = \ mock_wait_certificate # create a mock_consensus_state that returns a mock with # the following settings: mock_state = MockConsensusState.create_mock_consensus_state( claiming_too_frequently=True) mock_consensus_state.consensus_state_for_block_id.return_value = \ mock_state mock_consensus_state_store.return_value.__getitem__.return_value = \ mock_consensus_state # Create mock key state mock_poet_key_state_store.return_value.__getitem__.return_value = \ mock.Mock( sealed_signup_data='sealed signup data', has_been_refreshed=False) # create mock_signup_info mock_signup_info.unseal_signup_data.return_value = \ '00112233445566778899aabbccddeeff' # create mock_batch_publisher context = create_context('secp256k1') private_key = context.new_random_private_key() crypto_factory = CryptoFactory(context) signer = crypto_factory.new_signer(private_key) mock_batch_publisher = mock.Mock(identity_signer=signer) mock_block_cache = mock.MagicMock() mock_state_view_factory = mock.Mock() # create mock_block_header with the following fields mock_block = mock.Mock(identifier='0123456789abcdefedcba9876543210') mock_block.header.signer_public_key = \ '90834587139405781349807435098745' mock_block.header.previous_block_id = '2' mock_block.header.block_num = 1 mock_block.header.state_root_hash = '6' mock_block.header.batch_ids = '4' # check test with mock.patch('sawtooth_poet.poet_consensus.poet_block_publisher.' 'LOGGER') as mock_logger: block_publisher = \ poet_block_publisher.PoetBlockPublisher( block_cache=mock_block_cache, state_view_factory=mock_state_view_factory, batch_publisher=mock_batch_publisher, data_dir=self._temp_dir, config_dir=self._temp_dir, validator_id='validator_deadbeef') self.assertFalse( block_publisher.initialize_block( block_header=mock_block.header)) # Could be a hack, but verify that the appropriate log message is # generated - so we at least have some faith that the failure was # because of what we are testing and not something else. I know # that this is fragile if the log message is changed, so would # accept any suggestions on a better way to verify that the # function fails for the reason we expect. (message, *_), _ = mock_logger.info.call_args self.assertTrue('is claiming blocks too ' 'frequently' in message)
class SchedulerTester(object): """ The canonical form of the yaml is: - <------------------------------------------ batch start state_hash: string. Optional. No default. - <----------------------------------------- transaction start inputs: list of string. Required. - .... outputs: list of string. Required. - .... addresses_to_set: list of dict. Optional. - string <address>: Optional bytes <value> addresses_to_delete: list of str. Optional - string <address> valid: boolean. Optional. Defaults to True dependencies: list of string. Optional. Defaults to empty list. - ..... string. No default. If a dependency is the same string as a 'name' for another txn, that txn's signature will be used for the actual Transaction's dependency. If the string is not an 'name' of another txn, if it is longer than 20 characters it will be used as if is is the actual Transaction.header_signature for the dependency. If not, it will be disregarded. name: string. Optional. No default. """ def __init__(self, file_name): """ Args: file_name (str): The yaml filename and path. scheduler (scheduler.Scheduler): Any Scheduler implementaion context_manager (context_manager.ContextManager): The context manager holding state for this scheduler. """ self._context = create_context('secp256k1') self._crypto_factory = CryptoFactory(self._context) self._yaml_file_name = file_name self._counter = itertools.count(0) self._referenced_txns_in_other_batches = {} self._batch_id_by_txn_id = {} self._txn_execution = {} self._batch_results = {} self._batches = [] self._create_batches() @property def batch_results(self): """The batch results calculated from the yaml file. Returns: (dict): Computed from the yaml file, a dictionary with batch signature keys and BatchExecutionResult values. """ return self._batch_results def run_scheduler(self, scheduler, context_manager, validation_state_hash=None, txns_executed_fifo=True): """Add all the batches to the scheduler in order and then run through the txns in the scheduler, calling next_transaction() after each transaction_execution_result is set. Args: scheduler (scheduler.Scheduler): Any implementation of the Scheduler abstract base class. context_manager (context_manager.ContextManager): The context manager is needed to store state based on the yaml file. validation_state_hash (str): Used in cases where the yaml represents a single block of valid batches, and the state hash is not in the yaml file. This state hash is added to the last batch in the scheduler. Returns batch_results (list of tuples): A list of tuples of batch signature, BatchExecutionResult pairs. """ for i, batch in enumerate(self._batches): if i == len(self._batches) - 1 and \ validation_state_hash is not None: s_h = validation_state_hash else: s_h = self._batch_results[batch.header_signature].state_hash scheduler.add_batch(batch=batch, state_hash=s_h) scheduler.finalize() txns_to_process = deque() txn_context_by_txn_id = self._compute_transaction_execution_context() transactions_to_assert_state = {} while not scheduler.complete(block=False): stop = False while not stop: try: txn_info = scheduler.next_transaction() except StopIteration: break if txn_info is not None: txns_to_process.append(txn_info) LOGGER.debug("Transaction %s scheduled", txn_info.txn.header_signature[:16]) else: stop = True try: if txns_executed_fifo: t_info = txns_to_process.popleft() else: t_info = txns_to_process.pop() except IndexError: # No new txn was returned from next_transaction so # check again if complete. continue inputs, outputs = self._get_inputs_outputs(t_info.txn) c_id = context_manager.create_context( state_hash=t_info.state_hash, base_contexts=t_info.base_context_ids, inputs=inputs, outputs=outputs) t_id = t_info.txn.header_signature if t_id in txn_context_by_txn_id: state_up_to_now = txn_context_by_txn_id[t_id].state txn_context = txn_context_by_txn_id[t_id] inputs, _ = self._get_inputs_outputs(txn_context.txn) addresses = [input for input in inputs if len(input) == 70] state_found = context_manager.get( context_id=c_id, address_list=addresses) LOGGER.debug("Transaction Id %s, Batch %s, Txn %s, " "Context_id %s, Base Contexts %s", t_id[:16], txn_context.batch_num, txn_context.txn_num, c_id, t_info.base_context_ids) state_to_assert = [(add, state_up_to_now.get(add)) for add, _ in state_found] transactions_to_assert_state[t_id] = (txn_context, state_found, state_to_assert) validity, address_values, deletes = self._txn_execution[ t_info.txn.header_signature] context_manager.set( context_id=c_id, address_value_list=address_values) context_manager.delete( context_id=c_id, address_list=deletes) LOGGER.debug("Transaction %s is %s", t_id[:16], 'valid' if validity else 'invalid') scheduler.set_transaction_execution_result( txn_signature=t_info.txn.header_signature, is_valid=validity, context_id=c_id) batch_ids = [b.header_signature for b in self._batches] batch_results = [ (b_id, scheduler.get_batch_execution_result(b_id)) for b_id in batch_ids] return batch_results, transactions_to_assert_state def run_scheduler_alternating(self, scheduler, context_manager, validation_state_hash=None, txns_executed_fifo=True): batches = deque() batches.extend(self._batches) txns_to_process = deque() txn_context_by_txn_id = self._compute_transaction_execution_context() transactions_to_assert_state = {} while not scheduler.complete(block=False): stop = False while not stop: try: txn_info = scheduler.next_transaction() except StopIteration: stop = True if txn_info is not None: txns_to_process.append(txn_info) LOGGER.debug("Transaction %s scheduled", txn_info.txn.header_signature[:16]) else: stop = True try: scheduler.add_batch(batches.popleft()) except IndexError: scheduler.finalize() try: if txns_executed_fifo: t_info = txns_to_process.popleft() else: t_info = txns_to_process.pop() except IndexError: # No new txn was returned from next_transaction so # check again if complete. continue inputs, outputs = self._get_inputs_outputs(t_info.txn) c_id = context_manager.create_context( state_hash=t_info.state_hash, base_contexts=t_info.base_context_ids, inputs=inputs, outputs=outputs) t_id = t_info.txn.header_signature if t_id in txn_context_by_txn_id: state_up_to_now = txn_context_by_txn_id[t_id].state txn_context = txn_context_by_txn_id[t_id] inputs, _ = self._get_inputs_outputs(txn_context.txn) addresses = [input for input in inputs if len(input) == 70] state_found = context_manager.get( context_id=c_id, address_list=addresses) LOGGER.debug("Transaction Id %s, Batch %s, Txn %s, " "Context_id %s, Base Contexts %s", t_id[:16], txn_context.batch_num, txn_context.txn_num, c_id, t_info.base_context_ids) state_to_assert = [(add, state_up_to_now.get(add)) for add, _ in state_found] transactions_to_assert_state[t_id] = (txn_context, state_found, state_to_assert) validity, address_values, deletes = self._txn_execution[ t_info.txn.header_signature] context_manager.set( context_id=c_id, address_value_list=address_values) context_manager.delete( context_id=c_id, address_list=deletes) LOGGER.debug("Transaction %s is %s", t_id[:16], 'valid' if validity else 'invalid') scheduler.set_transaction_execution_result( txn_signature=t_info.txn.header_signature, is_valid=validity, context_id=c_id) batch_ids = [b.header_signature for b in self._batches] batch_results = [ (b_id, scheduler.get_batch_execution_result(b_id)) for b_id in batch_ids] return batch_results, transactions_to_assert_state def compute_state_hashes_wo_scheduler(self, base_dir): """Creates a state hash from the state updates from each txn in a valid batch. Returns state_hashes (list of str): The merkle roots from state changes in 1 or more blocks in the yaml file. """ database = NativeLmdbDatabase( os.path.join(base_dir, 'compute_state_hashes_wo_scheduler.lmdb'), indexes=MerkleDatabase.create_index_configuration(), _size=10 * 1024 * 1024) tree = MerkleDatabase(database=database) state_hashes = [] updates = {} for batch in self._batches: b_id = batch.header_signature result = self._batch_results[b_id] if result.is_valid: for txn in batch.transactions: txn_id = txn.header_signature _, address_values, deletes = self._txn_execution[txn_id] batch_updates = {} for pair in address_values: batch_updates.update({a: pair[a] for a in pair.keys()}) # since this is entirely serial, any overwrite # of an address is expected and desirable. updates.update(batch_updates) for address in deletes: if address in updates: del updates[address] # This handles yaml files that have state roots in them if result.state_hash is not None: s_h = tree.update(set_items=updates, virtual=False) tree.set_merkle_root(merkle_root=s_h) state_hashes.append(s_h) if not state_hashes: state_hashes.append(tree.update(set_items=updates)) return state_hashes def _compute_transaction_execution_context(self): """Compute the serial state for each txn in the yaml file up to and including the invalid txn in each invalid batch. Notes: The TransactionExecutionContext for a txn will contain the state applied serially up to that point for each valid batch and then for invalid batches up to the invalid txn. Returns: dict: The transaction id to the TransactionExecutionContext """ transaction_contexts = {} state_up_to_now = {} for batch_num, batch in enumerate(self._batches): partial_batch_transaction_contexts = {} partial_batch_state_up_to_now = state_up_to_now.copy() for txn_num, txn in enumerate(batch.transactions): t_id = txn.header_signature is_valid, address_values, deletes = self._txn_execution[t_id] partial_batch_transaction_contexts[t_id] = \ TransactionExecutionContext( txn=txn, txn_num=txn_num + 1, batch_num=batch_num + 1, state=partial_batch_state_up_to_now.copy()) for item in address_values: partial_batch_state_up_to_now.update(item) for address in deletes: if address in partial_batch_state_up_to_now: partial_batch_state_up_to_now[address] = None if not is_valid: break batch_id = batch.header_signature batch_is_valid = self._batch_results[batch_id].is_valid if batch_is_valid: transaction_contexts.update(partial_batch_transaction_contexts) state_up_to_now.update(partial_batch_state_up_to_now) return transaction_contexts def _address(self, add, require_full=False): if ':sha' not in add and ',' not in add: return add if ',' in add: return binascii.hexlify(bytearray( [int(i) for i in add.split(',')])) parts = add.split(':') if len(parts) == 3 and parts[2] == 'sha': # eg. 'yy:aaabbbb:sha' namespace = hashlib.sha512(parts[0].encode()).hexdigest()[:6] address = namespace + hashlib.sha512( parts[1].encode()).hexdigest()[:64] elif len(parts) == 3 and not require_full: # eg. 'a:sha:56' length = min(int(parts[2]), 70) address = hashlib.sha512(parts[0].encode()).hexdigest()[:length] elif len(parts) == 2: # eg. 'aaabbbb:sha' intermediate = parts[0] address = hashlib.sha512(intermediate.encode()).hexdigest()[:70] else: raise ValueError("Address specified by {} could " "not be formed".format(add)) return address def _get_inputs_outputs(self, txn): """Similarly to the TransactionExecutor, deserialize the inputs and outputs. Notes: The SchedulerTester has the inputs and outputs from the yaml file that it used to create the transaction, but it seems less error-prone to recreate the behavior of the TransactionExecutor. Args: txn (sawtooth_validator.protobuf.transaction_pb2.Transaction) Returns (tuple): (inputs, outputs) """ header = transaction_pb2.TransactionHeader() header.ParseFromString(txn.header) return list(header.inputs), list(header.outputs) def _bytes_if_none(self, value): if value is None: value = uuid.uuid4().hex.encode() return value def _yaml_from_file(self): with open(self._yaml_file_name, 'r') as infile: test_yaml = yaml.safe_load(infile) return test_yaml def _contains_and_not_none(self, key, obj): return key in obj and obj[key] is not None def _process_batches(self, yaml_batches, signer): batches = [] b_results = {} for batch in yaml_batches: batch_state_root = None if self._contains_and_not_none('state_hash', batch): batch_state_root = batch['state_hash'] txn_processing_result = self._process_txns( batch=batch, previous_batch_results=b_results.copy(), signer=signer) txns, batch_is_valid = txn_processing_result batch_real = create_batch( transactions=txns, signer=signer) for txn in txns: txn_id = txn.header_signature batch_id = batch_real.header_signature self._batch_id_by_txn_id[txn_id] = batch_id b_results[batch_real.header_signature] = BatchExecutionResult( is_valid=batch_is_valid, state_hash=batch_state_root) batches.append(batch_real) return batches, b_results def _dependencies_are_valid(self, dependencies, previous_batch_results): for dep in dependencies: if dep in self._batch_id_by_txn_id: batch_id = self._batch_id_by_txn_id[dep] dep_result = previous_batch_results[batch_id] if not dep_result.is_valid: return False return True def _process_txns(self, batch, previous_batch_results, signer): txns = [] referenced_txns = {} execution = {} batch_is_valid = True for transaction in batch: is_valid = True addresses_to_set = [] addresses_to_delete = [] inputs = transaction['inputs'] outputs = transaction['outputs'] inputs_real = [self._address(a) for a in inputs] outputs_real = [self._address(a) for a in outputs] if self._contains_and_not_none('addresses_to_set', transaction): addresses_to_set = [{ self._address(a, require_full=True): self._bytes_if_none( d[a]) for a in d } for d in transaction['addresses_to_set']] if self._contains_and_not_none('addresses_to_delete', transaction): addresses_to_delete = [ self._address(a, require_full=True) for a in transaction['addresses_to_delete'] ] if self._contains_and_not_none('dependencies', transaction): if any([ a not in self._referenced_txns_in_other_batches and len(a) <= 20 for a in transaction['dependencies'] ]): # This txn has a dependency with a txn signature that is # not known about, return None dependencies = [ self._referenced_txns_in_other_batches[a] if a in self._referenced_txns_in_other_batches else a for a in transaction['dependencies'] ] dependencies = [a for a in dependencies if len(a) > 20] else: dependencies = [] deps_valid = self._dependencies_are_valid( dependencies, previous_batch_results) if self._contains_and_not_none('valid', transaction): is_valid = bool(transaction['valid']) if not is_valid or not deps_valid: batch_is_valid = False txn, _ = create_transaction( payload=uuid.uuid4().hex.encode(), dependencies=dependencies, inputs=inputs_real, outputs=outputs_real, signer=signer) if self._contains_and_not_none('name', transaction): referenced_txns[transaction['name']] = txn.header_signature execution[txn.header_signature] = (is_valid, addresses_to_set, addresses_to_delete) txns.append(txn) self._txn_execution.update(execution) self._referenced_txns_in_other_batches.update(referenced_txns) return txns, batch_is_valid def _create_batches(self): test_yaml = self._yaml_from_file() private_key = self._context.new_random_private_key() signer = self._crypto_factory.new_signer(private_key) batches, batch_results = self._process_batches( yaml_batches=test_yaml, signer=signer) self._batch_results = batch_results self._batches = batches
def test_no_validator_registry( self, mock_utils, mock_validator_registry_view, mock_consensus_state, mock_poet_enclave_factory, mock_consensus_state_store, mock_poet_key_state_store, mock_signup_info, mock_poet_settings_view, mock_block_wrapper): """ Test verifies that PoET Block Publisher fails if a validator doesn't have any signup info in the validator registry (the validator is not listed in the validator registry) """ # create a mock_validator_registry_view that throws KeyError mock_validator_registry_view.return_value.get_validator_info. \ side_effect = KeyError('Non-existent validator') # create a mock_wait_certificate that does nothing in check_valid mock_wait_certificate = mock.Mock() mock_wait_certificate.check_valid.return_value = None mock_utils.deserialize_wait_certificate.return_value = \ mock_wait_certificate # create a mock_consensus_state that returns a mock with # the following settings: mock_state = MockConsensusState.create_mock_consensus_state() mock_consensus_state.consensus_state_for_block_id.return_value = \ mock_state mock_poet_key_state_store.return_value = \ _MockPoetKeyStateStore(active_key=None) # create mock_signup_info mock_signup_info.create_signup_info.return_value = \ mock.Mock( poet_public_key='poet public key', proof_data='proof data', anti_sybil_id='anti-sybil ID', sealed_signup_data='sealed signup data') mock_signup_info.block_id_to_nonce.return_value = 'nonce' # create mock_batch_publisher context = create_context('secp256k1') private_key = context.new_random_private_key() crypto_factory = CryptoFactory(context) signer = crypto_factory.new_signer(private_key) mock_batch_publisher = mock.Mock( identity_signer=signer) mock_block_cache = mock.MagicMock() mock_state_view_factory = mock.Mock() # create mock_block_header with the following fields mock_block = mock.Mock(identifier='0123456789abcdefedcba9876543210') mock_block.header.signer_public_key = \ '90834587139405781349807435098745' mock_block.header.previous_block_id = '2' mock_block.header.block_num = 1 mock_block.header.state_root_hash = '6' mock_block.header.batch_ids = '4' # check test block_publisher = \ poet_block_publisher.PoetBlockPublisher( block_cache=mock_block_cache, state_view_factory=mock_state_view_factory, batch_publisher=mock_batch_publisher, data_dir=self._temp_dir, config_dir=self._temp_dir, validator_id='validator_deadbeef') self.assertFalse( block_publisher.initialize_block( block_header=mock_block.header)) # check that batch publisher was called to send out # the txn header and txn for the validator registry update self.assertTrue(mock_batch_publisher.send.called)
class Messenger(object): def __init__(self, validator_url): self._connection = Connection(validator_url) self._context = create_context('secp256k1') self._crypto_factory = CryptoFactory(self._context) self._batch_signer = self._crypto_factory.new_signer( self._context.new_random_private_key()) def open_validator_connection(self): self._connection.open() def close_validator_connection(self): self._connection.close() def get_new_key_pair(self): private_key = self._context.new_random_private_key() public_key = self._context.get_public_key(private_key) return public_key.as_hex(), private_key.as_hex() async def send_create_agent_transaction(self, private_key, name, timestamp): transaction_signer = self._crypto_factory.new_signer( secp256k1.Secp256k1PrivateKey.from_hex(private_key)) batch = make_create_agent_transaction( transaction_signer=transaction_signer, batch_signer=self._batch_signer, name=name, timestamp=timestamp) await self._send_and_wait_for_commit(batch) async def send_create_record_transaction(self, private_key, device, seq, ts, ddata, dsize, dhash, record_id, timestamp): transaction_signer = self._crypto_factory.new_signer( secp256k1.Secp256k1PrivateKey.from_hex(private_key)) batch = make_create_record_transaction( transaction_signer=transaction_signer, batch_signer=self._batch_signer, seq=seq, ts=ts, device=device, ddata=ddata, dsize=dsize, dhash=dhash, record_id=record_id, timestamp=timestamp) await self._send_and_wait_for_commit(batch) async def send_transfer_record_transaction(self, private_key, receiving_agent, record_id, timestamp): transaction_signer = self._crypto_factory.new_signer( secp256k1.Secp256k1PrivateKey.from_hex(private_key)) batch = make_transfer_record_transaction( transaction_signer=transaction_signer, batch_signer=self._batch_signer, receiving_agent=receiving_agent, record_id=record_id, timestamp=timestamp) await self._send_and_wait_for_commit(batch) async def send_update_record_transaction(self, private_key, device, seq, ts, ddata, dsize, dhash, record_id, timestamp): transaction_signer = self._crypto_factory.new_signer( secp256k1.Secp256k1PrivateKey.from_hex(private_key)) batch = make_update_record_transaction( transaction_signer=transaction_signer, batch_signer=self._batch_signer, seq=seq, ts=ts, device=device, ddata=ddata, dsize=dsize, dhash=dhash, record_id=record_id, timestamp=timestamp) await self._send_and_wait_for_commit(batch) async def _send_and_wait_for_commit(self, batch): # Send transaction to validator submit_request = client_batch_submit_pb2.ClientBatchSubmitRequest( batches=[batch]) await self._connection.send( validator_pb2.Message.CLIENT_BATCH_SUBMIT_REQUEST, submit_request.SerializeToString()) # Send status request to validator batch_id = batch.header_signature status_request = client_batch_submit_pb2.ClientBatchStatusRequest( batch_ids=[batch_id], wait=True) validator_response = await self._connection.send( validator_pb2.Message.CLIENT_BATCH_STATUS_REQUEST, status_request.SerializeToString()) # Parse response status_response = client_batch_submit_pb2.ClientBatchStatusResponse() status_response.ParseFromString(validator_response.content) status = status_response.batch_statuses[0].status if status == client_batch_submit_pb2.ClientBatchStatus.INVALID: error = status_response.batch_statuses[0].invalid_transactions[0] raise ApiBadRequest(error.message) elif status == client_batch_submit_pb2.ClientBatchStatus.PENDING: raise ApiInternalError('Transaction submitted but timed out') elif status == client_batch_submit_pb2.ClientBatchStatus.UNKNOWN: raise ApiInternalError('Something went wrong. Try again later')
class Transactor: def __init__(self, name, rest_endpoint): """ Args: name (str): An identifier for this Transactor rest_endpoint (str): The rest api that this Transactor will communicate with. """ self.name = name self._rest_endpoint = rest_endpoint \ if rest_endpoint.startswith("http://") \ else "http://{}".format(rest_endpoint) with open('/root/.sawtooth/keys/{}.priv'.format(name)) as priv_file: private_key = Secp256k1PrivateKey.from_hex( priv_file.read().strip('\n')) self._signer = CryptoFactory(create_context('secp256k1')) \ .new_signer(private_key) self._factories = {} self._client = RestClient(url=self._rest_endpoint) self._add_transaction_family_factory(Families.INTKEY) self._add_transaction_family_factory(Families.XO) @property def public_key(self): return self._signer.get_public_key().as_hex() def _add_transaction_family_factory(self, family_name): """Add a MessageFactory for the specified family. Args: family_name (Families): One of the Enum values representing transaction families. """ family_config = FAMILY_CONFIG[family_name] self._factories[family_name] = MessageFactory( family_name=family_config['family_name'], family_version=family_config['family_version'], namespace=family_config['namespace'], signer=self._signer) def create_txn(self, family_name, batcher=None): unique_value = uuid4().hex[:20] encoder = TRANSACTION_ENCODER[family_name]['encoder'] payload = encoder( TRANSACTION_ENCODER[family_name]['payload_func'](unique_value)) address = TRANSACTION_ENCODER[family_name]['address_func']( unique_value) return self._factories[family_name].create_transaction( payload=payload, inputs=[address], outputs=[address], deps=[], batcher=batcher) def create_batch(self, family_name, count=1): transactions = [self.create_txn(family_name) for _ in range(count)] return self.batch_transactions(family_name, transactions=transactions) def batch_transactions(self, family_name, transactions): return self._factories[family_name].create_batch( transactions=transactions) def send(self, family_name, transactions=None): if not transactions: batch_list = self.create_batch(family_name) else: batch_list = self.batch_transactions(family_name=family_name, transactions=transactions) self._client.send_batches(batch_list=batch_list) def set_public_key_for_role(self, policy, role, permit_keys, deny_keys): permits = ["PERMIT_KEY {}".format(key) for key in permit_keys] denies = ["DENY_KEY {}".format(key) for key in deny_keys] self._run_identity_commands(policy, role, denies + permits) def _run_identity_commands(self, policy, role, rules): subprocess.run([ 'sawtooth', 'identity', 'policy', 'create', '-k', '/root/.sawtooth/keys/{}.priv'.format(self.name), '--wait', '15', '--url', self._rest_endpoint, policy, *rules ], check=True) subprocess.run([ 'sawtooth', 'identity', 'role', 'create', '-k', '/root/.sawtooth/keys/{}.priv'.format(self.name), '--wait', '15', '--url', self._rest_endpoint, role, policy ], check=True)
class SimpleTestClient: def __init__(self, baseUrl): self._baseUrl = baseUrl privateKey = Secp256k1PrivateKey.from_hex(privateKeyStr) self._signer = CryptoFactory(create_context('secp256k1')) \ .new_signer(privateKey) self._publicKey = self._signer.get_public_key().as_hex() self.publicKey = self._signer.get_public_key().as_hex() self._address = _hash(FAMILY_NAME.encode('utf-8'))[0:6] # _hash(self._publicKey.encode('utf-8'))[0:64] def wrap_and_send(self, data_dict): # rawPayload = action # for val in values: # rawPayload = ",".join([rawPayload, str(val)]) payload = json.dumps(data_dict).encode() address = self._address inputAddressList = [address] outputAddressList = [address] header = TransactionHeader( signer_public_key=self._publicKey, family_name=FAMILY_NAME, family_version="1.0", inputs=inputAddressList, outputs=outputAddressList, dependencies=[], payload_sha512=_hash(payload), batcher_public_key=self._publicKey, nonce=time.time().hex().encode() ).SerializeToString() transaction = Transaction( header=header, payload=payload, header_signature=self._signer.sign(header) ) transactionList = [transaction] header = BatchHeader( signer_public_key=self._publicKey, transaction_ids=[txn.header_signature for txn in transactionList] ).SerializeToString() batch = Batch( header=header, transactions=transactionList, header_signature=self._signer.sign(header)) batch_list = BatchList(batches=[batch]) return self._send_to_restapi( "batches", batch_list.SerializeToString(), 'application/octet-stream') def _send_to_restapi(self, suffix, data=None, contentType=None): if self._baseUrl.startswith("http://"): url = "{}/{}".format(self._baseUrl, suffix) else: url = "http://{}/{}".format(self._baseUrl, suffix) headers = {} if contentType is not None: headers['Content-Type'] = contentType try: if data is not None: result = requests.post(url, headers=headers, data=data) else: result = requests.get(url, headers=headers) if not result.ok: print('服务器错误') except requests.ConnectionError as err: print('连接失败',err) # raise SimpleWalletException( # 'Failed to connect to {}: {}'.format(url, str(err))) except BaseException as err: # raise SimpleWalletException(err) print(err) return result.text def get_state(self): result = self._send_to_restapi( "state/{}".format(self._address)) print(result) try: return base64.b64decode(yaml.safe_load(result)["data"]) except BaseException: return None def get_block(self): result = requests.get(self._baseUrl+'/blocks') datas = result.json()['data'] for data in datas: # print(data) payload = data['batches'][0]['transactions'][0]['payload'] # print(payload) p = base64.b64decode(payload.encode()).decode() try: print(json.loads(p)) except: pass
class CaClient: def __init__(self, base_url, keyfile=None): self._base_url = base_url if keyfile is None: self._signer = None return try: with open(keyfile) as fd: private_key_str = fd.read().strip() except OSError as err: raise XoException( 'Failed to read private key {}: {}'.format( keyfile, str(err))) try: private_key = Secp256k1PrivateKey.from_hex(private_key_str) except ParseError as e: raise XoException( 'Unable to load private key: {}'.format(str(e))) self._signer = CryptoFactory(create_context('secp256k1')) \ .new_signer(private_key) def create(self, csr, wait=None, auth_user=None, auth_password=None): return self._send_ca_txn( "create", value=csr, wait=wait, auth_user=auth_user, auth_password=auth_password) def init(self, pkey, wait=None, auth_user=None, auth_password=None): return self._send_ca_txn( "init", value=pkey, wait=wait, auth_user=auth_user, auth_password=auth_password) def simple(self, data, wait=None, auth_user=None, auth_password=None): return self._send_simple_txn( value=data, wait=wait, auth_user=auth_user, auth_password=auth_password) def get(self, serial: str, wait=None, auth_user=None, auth_password=None): return self._send_ca_txn( "get", value=serial, wait=wait, auth_user=auth_user, auth_password=auth_password) def status(self, serial: str, wait=None, auth_user=None, auth_password=None): return self._send_ca_txn( "status", value=serial, wait=wait, auth_user=auth_user, auth_password=auth_password) def revoke(self, serial: str, wait=None, auth_user=None, auth_password=None): return self._send_ca_txn( "revoke", value=serial, wait=wait, auth_user=auth_user, auth_password=auth_password) def _get_status(self, batch_id, wait, auth_user=None, auth_password=None): try: result = self._send_request( 'batch_statuses?id={}&wait={}'.format(batch_id, wait), auth_user=auth_user, auth_password=auth_password) return yaml.safe_load(result)['data'][0]['status'] except BaseException as err: raise XoException(err) def _get_prefix(self): return _sha512('ca_1'.encode('utf-8'))[:6] def _get_address(self, name): xo_prefix = self._get_prefix() game_address = _sha512(name.encode('utf-8'))[:64] return xo_prefix + game_address def _get_simple_address(self, data): prefix = hashlib.sha512('simple'.encode('utf-8')).hexdigest()[:6] address = hashlib.sha512(data.encode('utf-8')).hexdigest()[:64] return prefix + address def _send_request(self, suffix, data=None, content_type=None, name=None, auth_user=None, auth_password=None): if self._base_url.startswith("http://"): url = "{}/{}".format(self._base_url, suffix) else: url = "http://{}/{}".format(self._base_url, suffix) headers = {} if auth_user is not None: auth_string = "{}:{}".format(auth_user, auth_password) b64_string = b64encode(auth_string.encode()).decode() auth_header = 'Basic {}'.format(b64_string) headers['Authorization'] = auth_header if content_type is not None: headers['Content-Type'] = content_type try: if data is not None: result = requests.post(url, headers=headers, data=data) else: result = requests.get(url, headers=headers) if result.status_code == 404: raise XoException("No such game: {}".format(name)) elif not result.ok: raise XoException("Error {}: {}".format( result.status_code, result.reason)) except requests.ConnectionError as err: raise XoException( 'Failed to connect to {}: {}'.format(url, str(err))) except BaseException as err: raise XoException(err) return result.text def _send_ca_txn(self, action, value="", wait=None, auth_user=None, auth_password=None): # Serialization is just a delimited utf-8 encoded string payload = "|".join([action, datetime.datetime.utcnow().isoformat(), str(value)]).encode('utf-8') # Construct the address address = self._get_prefix() header = TransactionHeader( signer_public_key=self._signer.get_public_key().as_hex(), family_name="CA", family_version="1.0", inputs=[address], outputs=[address], dependencies=[], payload_sha512=_sha512(payload), batcher_public_key=self._signer.get_public_key().as_hex(), nonce=hex(random.randint(0, 2**64)) ).SerializeToString() signature = self._signer.sign(header) transaction = Transaction( header=header, payload=payload, header_signature=signature ) batch_list = self._create_batch_list([transaction]) batch_id = batch_list.batches[0].header_signature if wait and wait > 0: wait_time = 0 start_time = time.time() response = self._send_request( "batches", batch_list.SerializeToString(), 'application/octet-stream', auth_user=auth_user, auth_password=auth_password) while wait_time < wait: status = self._get_status( batch_id, wait - int(wait_time), auth_user=auth_user, auth_password=auth_password) wait_time = time.time() - start_time if status != 'PENDING': return response return response return self._send_request( "batches", batch_list.SerializeToString(), 'application/octet-stream', auth_user=auth_user, auth_password=auth_password) def _send_simple_txn(self, value="", wait=None, auth_user=None, auth_password=None): # Serialization is just a delimited utf-8 encoded string payload = str(value).encode() # Construct the address address = self._get_simple_address('Simple Data Value 1') header = TransactionHeader( signer_public_key=self._signer.get_public_key().as_hex(), family_name='SIMPLE', family_version="1.0", inputs=[address], outputs=[address], dependencies=[], payload_sha512=_sha512(payload), batcher_public_key=self._signer.get_public_key().as_hex(), nonce=hex(random.randint(0, 2**64)) ).SerializeToString() signature = self._signer.sign(header) transaction = Transaction( header=header, payload=payload, header_signature=signature ) batch_list = self._create_batch_list([transaction]) batch_id = batch_list.batches[0].header_signature if wait and wait > 0: wait_time = 0 start_time = time.time() response = self._send_request( "batches", batch_list.SerializeToString(), 'application/octet-stream', auth_user=auth_user, auth_password=auth_password) while wait_time < wait: status = self._get_status( batch_id, wait - int(wait_time), auth_user=auth_user, auth_password=auth_password) wait_time = time.time() - start_time if status != 'PENDING': return response return response return self._send_request( "batches", batch_list.SerializeToString(), 'application/octet-stream', auth_user=auth_user, auth_password=auth_password) def _create_batch_list(self, transactions): transaction_signatures = [t.header_signature for t in transactions] header = BatchHeader( signer_public_key=self._signer.get_public_key().as_hex(), transaction_ids=transaction_signatures ).SerializeToString() signature = self._signer.sign(header) batch = Batch( header=header, transactions=transactions, header_signature=signature) return BatchList(batches=[batch]) def subscribe(self, event_name: str, is_write_to_file=False, file_name='certificate.pem'): subscription = EventSubscription(event_type="ca_1/{}".format(event_name)) # Setup a connection to the validator ctx = zmq.Context() socket = ctx.socket(zmq.DEALER) socket.connect('tcp://127.0.0.1:4004') # Construct the request request = ClientEventsSubscribeRequest( subscriptions=[subscription]).SerializeToString() # Construct the message wrapper correlation_id = "123" # This must be unique for all in-process requests msg = Message( correlation_id=correlation_id, message_type=Message.CLIENT_EVENTS_SUBSCRIBE_REQUEST, content=request) # Send the request socket.send_multipart([msg.SerializeToString()]) # Receive the response resp = socket.recv_multipart()[-1] # Parse the message wrapper msg = Message() msg.ParseFromString(resp) # Validate the response type if msg.message_type != Message.CLIENT_EVENTS_SUBSCRIBE_RESPONSE: print("Unexpected message type") return # Parse the response response = ClientEventsSubscribeResponse() response.ParseFromString(msg.content) # Validate the response status if response.status != ClientEventsSubscribeResponse.OK: print("Subscription failed: {}".format(response.response_message)) return resp = socket.recv_multipart()[-1] # Parse the message wrapper msg = Message() msg.ParseFromString(resp) # Validate the response type if msg.message_type != Message.CLIENT_EVENTS: print("Unexpected message type") return # Parse the response events = EventList() events.ParseFromString(msg.content) for event in events.events: if event.data is not None: if is_write_to_file: write_to_file(file_name, event.data) else: print(event.data) # Construct the request request = ClientEventsUnsubscribeRequest().SerializeToString() # Construct the message wrapper correlation_id = "124" # This must be unique for all in-process requests msg = Message( correlation_id=correlation_id, message_type=Message.CLIENT_EVENTS_UNSUBSCRIBE_REQUEST, content=request) # Send the request socket.send_multipart([msg.SerializeToString()]) # Receive the response resp = socket.recv_multipart()[-1] # Parse the message wrapper msg = Message() msg.ParseFromString(resp) # Validate the response type if msg.message_type != Message.CLIENT_EVENTS_UNSUBSCRIBE_RESPONSE: print("Unexpected message type") # Parse the response response = ClientEventsUnsubscribeResponse() response.ParseFromString(msg.content) # Validate the response status if response.status != ClientEventsUnsubscribeResponse.OK: print("Unsubscription failed: {}".format(response.response_message)) # Close the connection to the validator socket.close()
def test_block_publisher_finalize_block( self, mock_utils, mock_validator_registry_view, mock_consensus_state, mock_poet_enclave_factory, mock_consensus_state_store, mock_poet_key_state_store, mock_signup_info, mock_wait_certificate, mock_poet_settings_view, mock_block_wrapper): """ Test verifies that PoET Block Publisher finalizes the block, meaning that the candidate block is good and should be generated. """ # create a mock_validator_registry_view with # get_validator_info that does nothing mock_validator_registry_view.return_value.get_validator_info. \ return_value = \ ValidatorInfo( name='validator_001', id='validator_deadbeef', signup_info=SignUpInfo( poet_public_key='00112233445566778899aabbccddeeff')) # create a mock_wait_certificate that does nothing in check_valid my_wait_certificate = mock.Mock() my_wait_certificate.check_valid.return_value = None mock_wait_certificate.create_wait_certificate.return_value = \ my_wait_certificate # create a mock_consensus_state that returns a mock with # the following settings: mock_state = MockConsensusState().create_mock_consensus_state() mock_consensus_state.consensus_state_for_block_id.return_value = \ mock_state # create mock_batch_publisher context = create_context('secp256k1') private_key = context.new_random_private_key() crypto_factory = CryptoFactory(context) signer = crypto_factory.new_signer(private_key) mock_batch_publisher = mock.Mock( identity_signer=signer) mock_block_cache = mock.MagicMock() mock_state_view_factory = mock.Mock() # create mock_block_header with the following fields mock_block = mock.Mock(identifier='0123456789abcdefedcba9876543210') mock_block.header.signer_public_key = \ '90834587139405781349807435098745' mock_block.header.previous_block_id = '2' mock_block.header.block_num = 1 mock_block.header.state_root_hash = '6' mock_block.header.batch_ids = '4' # check test block_publisher = \ poet_block_publisher.PoetBlockPublisher( block_cache=mock_block_cache, state_view_factory=mock_state_view_factory, batch_publisher=mock_batch_publisher, data_dir=self._temp_dir, config_dir=self._temp_dir, validator_id='validator_deadbeef') with mock.patch('sawtooth_poet.poet_consensus.' 'poet_block_publisher.json') as _: self.assertTrue(block_publisher.finalize_block( block_header=mock_block.header))
def get_new_signer(cls): context = create_context('secp256k1') return CryptoFactory(context).new_signer( context.new_random_private_key())
class CCellularClient: def __init__(self, conf: DistributedManagerConfig): self.conf = conf self.url = conf.CLIENT_URL # Transaction batcher self.transaction_queue = queue.Queue() self.pending_transactions = [] self.run_check = None self.transaction_batcher_thread = None self.last_batch_time = 0 # Get the key from the local machine keyfile = conf.CLIENT_KEY_PATH # The signer requires a key from the local user/machine # See sawtooth documentation for producing a key if keyfile is not None: try: with open(keyfile) as fd: private_key_str = fd.read().strip() fd.close() except OSError as err: raise Exception('Failed to read private key at {}'.format(err)) try: private_key = Secp256k1PrivateKey.from_hex(private_key_str) except ParseError as e: raise Exception( 'Unable to load the private key correctly {}'.format( str(e))) self._signer = CryptoFactory( create_context('secp256k1')).new_signer(private_key) self.logger = None # Spawns a thread that handles the creation of transactions and batching # Run check function is used by the batcher to check if parent module is still running def start_batcher(self, run_check_function): if run_check_function is None: raise ValueError("Run check function must be specified") self.run_check = run_check_function self.transaction_batcher_thread = threading.Thread( target=self._transaction_batcher) self.transaction_batcher_thread.start() # Creates a transaction and sends to sawtooth validator def set_entry(self, entry: DatabaseEntry): self.log("Set entry called, queueing transaction") self.transaction_queue.put(('set', entry)) # Get value of the entry corresponding to the key def get(self, key): self.log("Get entry called with key: " + str(key)) address = make_ccellular_address(key) result = self._send_request("state/{}".format(address), name=key) if result != None: try: json_result = json.loads(result) data_response = json_result['data'] b64data = yaml.safe_load(data_response) b64decoded = base64.b64decode(b64data) cbor_decoded = cbor.loads(b64decoded) return DatabaseEntry(cbor_decoded[key]) except BaseException as e: print("Received a base exception:", e) return None # Will likely get all data, may need to be optimized def get_all(self): self.log("Getting all keys") prefix = get_prefix() # For the transaction family result = self._send_request("state", name="GET_ALL_KEYS", params={"address": prefix}) if result != None: try: self.log("json result: " + str(json.loads(result))) next_page = json.loads(result)['paging'].get('next') json_result_list = json.loads(result)['data'] entry_key_set = set() for result in json_result_list: data_response = result['data'] b64data = yaml.safe_load(data_response) b64decoded = base64.b64decode(b64data) cbor_decoded = cbor.loads(b64decoded) entry_key_set.update(set(cbor_decoded.keys())) # Get paginated elements while next_page != None: self.log("Getting paginated element") result_obj = requests.get(next_page) if result_obj != None: if not result_obj.ok: self.log("Error getting paginated list -- {}: {}". format(result_obj.status_code, result_obj.reason)) break result = result_obj.text next_page = json.loads(result)['paging'].get('next') json_result_list = json.loads(result)['data'] self.log("json result (pag): " + str(json.loads(result))) for result in json_result_list: data_response = result['data'] b64data = yaml.safe_load(data_response) b64decoded = base64.b64decode(b64data) cbor_decoded = cbor.loads(b64decoded) entry_key_set.update(set(cbor_decoded.keys())) return entry_key_set except BaseException as e: self.log("Received a base exception: " + str(e)) return None # Processes available transactions into batches # Batch size and timeout can be configured def _transaction_batcher(self): self.log("Batcher thread entering") while self.run_check(): # get any new transactions (up to batch size) while self.transaction_queue.qsize() > 0: # get the next pending transaction and add it to pending action, entry = self.transaction_queue.get() self._build_transaction(action, entry) # stop adding new messages if batch size is reached # TODO: possible overloading option? if len(self.pending_transactions) >= self.conf.BATCH_SIZE: break # if there are no transactions, reset the batch timeout if len(self.pending_transactions) == 0: self.last_batch_time = time.time() # check for a new full size batch or a batch timeout with at least one transaction elif len(self.pending_transactions) >= self.conf.BATCH_SIZE or\ (time.time() - self.last_batch_time >= self.conf.BATCH_TIMEOUT and\ len(self.pending_transactions) > 0): self.log("Creating and sending new batch") if len(self.pending_transactions) >= self.conf.BATCH_SIZE: self.log(" Batch size reached") else: self.log(" Batch timeout exceeded, sending with {0}/{1}"\ .format(len(self.pending_transactions), self.conf.BATCH_SIZE)) # build batch from available transactions batch_list = self._create_batch_list(self.pending_transactions) # continue and retry later if there is no batch to send if batch_list is None: self.log("No batch list to send") continue # clear the pending list of transactions self.pending_transactions = [] # Send new batch (TODO save output?) self._send_request( "batches", batch_list.SerializeToString(), 'application/octet-stream', ) # wait a little before checking again (for performance) time.sleep(self.conf.BATCH_CHECK_DELAY) self.log("Batcher thread exiting") self.transaction_batcher_thread = None # Builds a transaction from the provided info and adds to pending def _build_transaction(self, action, entry): payload = build_payload(action, entry) address = make_ccellular_address(entry.key()) header = TransactionHeader( signer_public_key=self._signer.get_public_key().as_hex(), family_name=self.conf.FAMILY_NAME, family_version=self.conf.FAMILY_VERSION, inputs=[address], outputs=[address], dependencies=[], payload_sha512=_sha512(payload), batcher_public_key=self._signer.get_public_key().as_hex(), nonce=hex(random.randint(0, 2**64))).SerializeToString() signature = self._signer.sign(header) transaction = Transaction(header=header, payload=payload, header_signature=signature) self.pending_transactions.append(transaction) # Creates a transaction batch of one or more transactions # All transactions must be included in a batch def _create_batch_list(self, transactions): if len(transactions) < 1: self.log( "Attempting to create batch with no transactions, ignoring") return None transaction_signatures = [t.header_signature for t in transactions] header = BatchHeader( signer_public_key=self._signer.get_public_key().as_hex(), transaction_ids=transaction_signatures).SerializeToString() signature = self._signer.sign(header) batch = Batch(header=header, transactions=transactions, header_signature=signature) return BatchList(batches=[batch]) # Sends the request to the validator def _send_request(self, suffix, data=None, content_type=None, name=None, params={}): if self.url.startswith("http://"): url = "{}/{}".format(self.url, suffix) else: url = "http://{}/{}".format(self.url, suffix) headers = {} if content_type is not None: headers['Content-Type'] = content_type try: if data is not None: result = requests.post(url, headers=headers, data=data) else: result = requests.get(url, headers=headers, params=params) if result.status_code == 404: self.log("No such Key Exists: {}".format(name)) return None if not result.ok: raise Exception("Error {}: {}".format(result.status_code, result.reason)) except requests.ConnectionError as err: raise Exception( "Failed to connect to the REST API services : {}".format(err)) except BaseException as err: raise Exception("Failed {}".format(err)) return result.text def log(self, message): if self.logger: self.logger("(Client) " + message)
def load_config(appl): # pylint: disable=too-many-branches appl.config.update(DEFAULT_CONFIG) config_file_path = os.path.join( os.path.dirname(os.path.dirname(os.path.realpath(__file__))), 'config.py') try: appl.config.from_pyfile(config_file_path) except FileNotFoundError: LOGGER.warning("No config file provided") # CLI Options will override config file options opts = parse_args(sys.argv[1:]) if opts.host is not None: appl.config.HOST = opts.host if opts.port is not None: appl.config.PORT = opts.port if opts.timeout is not None: appl.config.TIMEOUT = opts.timeout if opts.validator is not None: appl.config.VALIDATOR_URL = opts.validator # if opts.db_host is not None: # app.config.DB_HOST = opts.db_host # if opts.db_port is not None: # app.config.DB_PORT = opts.db_port # if opts.db_name is not None: # app.config.DB_NAME = opts.db_name if opts.debug is not None: appl.config.DEBUG = opts.debug if opts.secret_key is not None: appl.config.SECRET_KEY = opts.secret_key if appl.config.SECRET_KEY is None: LOGGER.exception("API secret key was not provided") sys.exit(1) if opts.aes_key is not None: appl.config.AES_KEY = opts.aes_key if appl.config.AES_KEY is None: LOGGER.exception("AES key was not provided") sys.exit(1) if opts.batcher_private_key is not None: appl.config.BATCHER_PRIVATE_KEY = opts.batcher_private_key if appl.config.BATCHER_PRIVATE_KEY is None: LOGGER.exception("Batcher private key was not provided") sys.exit(1) if opts.batcher_private_key_file_name_clinic is not None: appl.config.BATCHER_PRIVATE_KEY_FILE_NAME_CLINIC = opts.batcher_private_key_file_name_clinic if opts.batcher_private_key_file_name_doctor is not None: appl.config.BATCHER_PRIVATE_KEY_FILE_NAME_DOCTOR = opts.batcher_private_key_file_name_doctor if opts.batcher_private_key_file_name_patient is not None: appl.config.BATCHER_PRIVATE_KEY_FILE_NAME_PATIENT = opts.batcher_private_key_file_name_patient if opts.batcher_private_key_file_name_lab is not None: appl.config.BATCHER_PRIVATE_KEY_FILE_NAME_LAB = opts.batcher_private_key_file_name_lab if opts.batcher_private_key_file_name_insurance is not None: appl.config.BATCHER_PRIVATE_KEY_FILE_NAME_INSURANCE = opts.batcher_private_key_file_name_insurance if appl.config.BATCHER_PRIVATE_KEY_FILE_NAME_CLINIC is None: LOGGER.exception( "Batcher private key file name for Clinic entity was not provided") sys.exit(1) if appl.config.BATCHER_PRIVATE_KEY_FILE_NAME_DOCTOR is None: LOGGER.exception( "Batcher private key file name for Doctor entity was not provided") sys.exit(1) if appl.config.BATCHER_PRIVATE_KEY_FILE_NAME_PATIENT is None: LOGGER.exception( "Batcher private key file name for Patient entity was not provided" ) sys.exit(1) if appl.config.BATCHER_PRIVATE_KEY_FILE_NAME_LAB is None: LOGGER.exception( "Batcher private key file name for Lab entity was not provided") sys.exit(1) if appl.config.BATCHER_PRIVATE_KEY_FILE_NAME_INSURANCE is None: LOGGER.exception( "Batcher private key file name for Insurance entity was not provided" ) sys.exit(1) try: private_key_file_name_clinic = get_keyfile( appl.config.BATCHER_PRIVATE_KEY_FILE_NAME_CLINIC) clinic_private_key = get_signer_from_file(private_key_file_name_clinic) private_key_file_name_doctor = get_keyfile( appl.config.BATCHER_PRIVATE_KEY_FILE_NAME_DOCTOR) doctor_private_key = get_signer_from_file(private_key_file_name_doctor) private_key_file_name_patient = get_keyfile( appl.config.BATCHER_PRIVATE_KEY_FILE_NAME_PATIENT) patient_private_key = get_signer_from_file( private_key_file_name_patient) private_key_file_name_lab = get_keyfile( appl.config.BATCHER_PRIVATE_KEY_FILE_NAME_LAB) lab_private_key = get_signer_from_file(private_key_file_name_lab) private_key_file_name_insurance = get_keyfile( appl.config.BATCHER_PRIVATE_KEY_FILE_NAME_INSURANCE) insurance_private_key = get_signer_from_file( private_key_file_name_insurance) # private_key = Secp256k1PrivateKey.from_hex( # app.config.BATCHER_PRIVATE_KEY) except ParseError as err: LOGGER.exception('Unable to load private key: %s', str(err)) sys.exit(1) appl.config.CONTEXT = create_context('secp256k1') appl.config.SIGNER_CLINIC = CryptoFactory( appl.config.CONTEXT).new_signer(clinic_private_key) appl.config.SIGNER_DOCTOR = CryptoFactory( appl.config.CONTEXT).new_signer(doctor_private_key) appl.config.SIGNER_PATIENT = CryptoFactory( appl.config.CONTEXT).new_signer(patient_private_key) appl.config.SIGNER_LAB = CryptoFactory( appl.config.CONTEXT).new_signer(lab_private_key) appl.config.SIGNER_INSURANCE = CryptoFactory( appl.config.CONTEXT).new_signer(insurance_private_key)
class Transactor(object): def __init__(self, name, rest_endpoint): """ Args: name (str): An identifier for this Transactor rest_endpoint (str): The rest api that this Transactor will communicate with. """ self.name = name self._rest_endpoint = rest_endpoint \ if rest_endpoint.startswith("http://") \ else "http://{}".format(rest_endpoint) with open('/root/.sawtooth/keys/{}.priv'.format(name)) as priv_file: private_key = Secp256k1PrivateKey.from_hex( priv_file.read().strip('\n')) self._signer = CryptoFactory(create_context('secp256k1')) \ .new_signer(private_key) self._factories = {} self._client = RestClient(url=self._rest_endpoint) self._add_transaction_family_factory(Families.INTKEY) self._add_transaction_family_factory(Families.XO) @property def public_key(self): return self._signer.get_public_key().as_hex() def _add_transaction_family_factory(self, family_name): """Add a MessageFactory for the specified family. Args: family_name (Families): One of the Enum values representing transaction families. """ family_config = FAMILY_CONFIG[family_name] self._factories[family_name] = MessageFactory( family_name=family_config['family_name'], family_version=family_config['family_version'], namespace=family_config['namespace'], signer=self._signer) def create_txn(self, family_name, batcher=None): unique_value = uuid4().hex[:20] encoder = TRANSACTION_ENCODER[family_name]['encoder'] payload = encoder( TRANSACTION_ENCODER[family_name]['payload_func'](unique_value)) address = TRANSACTION_ENCODER[family_name]['address_func']( unique_value) return self._factories[family_name].create_transaction( payload=payload, inputs=[address], outputs=[address], deps=[], batcher=batcher) def create_batch(self, family_name, count=1): transactions = [self.create_txn(family_name) for _ in range(count)] return self.batch_transactions(family_name, transactions=transactions) def batch_transactions(self, family_name, transactions): return self._factories[family_name].create_batch( transactions=transactions) def send(self, family_name, transactions=None): if not transactions: batch_list = self.create_batch(family_name) else: batch_list = self.batch_transactions( family_name=family_name, transactions=transactions) self._client.send_batches(batch_list=batch_list) def set_public_key_for_role(self, policy, role, permit_keys, deny_keys): permits = ["PERMIT_KEY {}".format(key) for key in permit_keys] denies = ["DENY_KEY {}".format(key) for key in deny_keys] self._run_identity_commands(policy, role, denies + permits) def _run_identity_commands(self, policy, role, rules): subprocess.run( ['sawtooth', 'identity', 'policy', 'create', '-k', '/root/.sawtooth/keys/{}.priv'.format(self.name), '--wait', '15', '--url', self._rest_endpoint, policy, *rules], check=True) subprocess.run( ['sawtooth', 'identity', 'role', 'create', '-k', '/root/.sawtooth/keys/{}.priv'.format(self.name), '--wait', '15', '--url', self._rest_endpoint, role, policy], check=True)
def get_signer(): context = create_context('secp256k1') private_key = context.new_random_private_key() crypto_factory = CryptoFactory(context) return crypto_factory.new_signer(private_key)
class SupplierBatch: def __init__(self, base_url, keyfile=None): self._base_url = base_url if keyfile is None: self._signer = None return try: with open(keyfile) as fd: private_key_str = fd.read().strip() except OSError as err: raise SupplierException( 'Failed to read private key {}: {}'.format( keyfile, str(err))) try: private_key = Secp256k1PrivateKey.from_hex(private_key_str) except ParseError as e: raise SupplierException( 'Unable to load private key: {}'.format(str(e))) self._signer = CryptoFactory(create_context('secp256k1')) \ .new_signer(private_key) def create(self,supplier_id,short_id,supplier_name,passwd,supplier_url, auth_user=None, auth_password=None): return self.create_supplier_transaction(supplier_id,short_id,supplier_name,passwd,supplier_url, "create", auth_user=auth_user, auth_password=auth_password) def add_part(self,supplier_id,part_id): return self.create_supplier_transaction(supplier_id,"","","","","AddPart",part_id) def list_supplier(self, auth_user=None, auth_password=None): supplier_prefix = self._get_prefix() result = self._send_request( "state?address={}".format(supplier_prefix), auth_user=auth_user, auth_password=auth_password ) try: encoded_entries = yaml.safe_load(result)["data"] return [ base64.b64decode(entry["data"]) for entry in encoded_entries ] except BaseException: return None def retrieve_supplier(self, supplier_id, auth_user=None, auth_password=None): address = self._get_address(supplier_id) result = self._send_request("state/{}".format(address), supplier_id=supplier_id, auth_user=auth_user, auth_password=auth_password) try: return base64.b64decode(yaml.safe_load(result)["data"]) except BaseException: return None def _get_status(self, batch_id, wait, auth_user=None, auth_password=None): try: result = self._send_request( 'batch_statuses?id={}&wait={}'.format(batch_id, wait), auth_user=auth_user, auth_password=auth_password) return yaml.safe_load(result)['data'][0]['status'] except BaseException as err: raise SupplierException(err) def _get_prefix(self): return _sha512('supplier'.encode('utf-8'))[0:6] def _get_address(self, supplier_id): supplier_prefix = self._get_prefix() address = _sha512(supplier_id.encode('utf-8'))[0:64] return supplier_prefix + address def _send_request( self, suffix, data=None, content_type=None, supplier_id=None, auth_user=None, auth_password=None): if self._base_url.startswith("http://"): url = "{}/{}".format(self._base_url, suffix) else: url = "http://{}/{}".format(self._base_url, suffix) headers = {} if auth_user is not None: auth_string = "{}:{}".format(auth_user, auth_password) b64_string = b64encode(auth_string.encode()).decode() auth_header = 'Basic {}'.format(b64_string) headers['Authorization'] = auth_header if content_type is not None: headers['Content-Type'] = content_type try: if data is not None: result = requests.post(url, headers=headers, data=data) else: result = requests.get(url, headers=headers) if result.status_code == 404: raise SupplierException("No such supplier: {}".format(supplier_id)) elif not result.ok: raise SupplierException("Error {}: {}".format( result.status_code, result.reason)) except BaseException as err: raise SupplierException(err) return result.text def create_supplier_transaction(self, supplier_id,short_id="",supplier_name="",passwd="",supplier_url="", action="",part_id="", auth_user=None, auth_password=None): payload = ",".join([supplier_id,str(short_id),str(supplier_name),str(passwd),str(supplier_url), action,str(part_id)]).encode() # Construct the address address = self._get_address(supplier_id) header = TransactionHeader( signer_pubkey=self._signer.get_public_key().as_hex(), family_name="supplier", family_version="1.0", inputs=[address], outputs=[address], dependencies=[], payload_encoding="csv-utf8", payload_sha512=_sha512(payload), batcher_pubkey=self._signer.get_public_key().as_hex(), nonce=time.time().hex().encode() ).SerializeToString() signature = self._signer.sign(header) transaction = Transaction( header=header, payload=payload, header_signature=signature ) batch_list = self._create_batch_list([transaction]) batch_id = batch_list.batches[0].header_signature return self._send_request( "batches", batch_list.SerializeToString(), 'application/octet-stream', auth_user=auth_user, auth_password=auth_password ) def _create_batch_list(self, transactions): transaction_signatures = [t.header_signature for t in transactions] header = BatchHeader( signer_public_key=self._signer.get_public_key().as_hex(), transaction_ids=transaction_signatures ).SerializeToString() signature = self._signer.sign(header) batch = Batch( header=header, transactions=transactions, header_signature=signature ) return BatchList(batches=[batch])
def make_key(): context = create_context('secp256k1') private_key = context.new_random_private_key() signer = CryptoFactory(context).new_signer(private_key) return signer
class XoClient: def __init__(self, base_url, keyfile=None): self._base_url = base_url if keyfile is None: self._signer = None return try: with open(keyfile) as fd: private_key_str = fd.read().strip() except OSError as err: raise XoException( 'Failed to read private key {}: {}'.format( keyfile, str(err))) try: private_key = Secp256k1PrivateKey.from_hex(private_key_str) except ParseError as e: raise XoException( 'Unable to load private key: {}'.format(str(e))) self._signer = CryptoFactory(create_context('secp256k1')) \ .new_signer(private_key) def create(self, name, wait=None, auth_user=None, auth_password=None): return self._send_xo_txn( name, "create", wait=wait, auth_user=auth_user, auth_password=auth_password) def delete(self, name, wait=None, auth_user=None, auth_password=None): return self._send_xo_txn( name, "delete", wait=wait, auth_user=auth_user, auth_password=auth_password) def take(self, name, space, wait=None, auth_user=None, auth_password=None): return self._send_xo_txn( name, "take", space, wait=wait, auth_user=auth_user, auth_password=auth_password) def list(self, auth_user=None, auth_password=None): xo_prefix = self._get_prefix() result = self._send_request( "state?address={}".format(xo_prefix), auth_user=auth_user, auth_password=auth_password) try: encoded_entries = yaml.safe_load(result)["data"] return [ base64.b64decode(entry["data"]) for entry in encoded_entries ] except BaseException: return None def show(self, name, auth_user=None, auth_password=None): address = self._get_address(name) result = self._send_request( "state/{}".format(address), name=name, auth_user=auth_user, auth_password=auth_password) try: return base64.b64decode(yaml.safe_load(result)["data"]) except BaseException: return None def _get_status(self, batch_id, wait, auth_user=None, auth_password=None): try: result = self._send_request( 'batch_statuses?id={}&wait={}'.format(batch_id, wait), auth_user=auth_user, auth_password=auth_password) return yaml.safe_load(result)['data'][0]['status'] except BaseException as err: raise XoException(err) def _get_prefix(self): return _sha512('xo'.encode('utf-8'))[0:6] def _get_address(self, name): xo_prefix = self._get_prefix() game_address = _sha512(name.encode('utf-8'))[0:64] return xo_prefix + game_address def _send_request(self, suffix, data=None, content_type=None, name=None, auth_user=None, auth_password=None): if self._base_url.startswith("http://"): url = "{}/{}".format(self._base_url, suffix) else: url = "http://{}/{}".format(self._base_url, suffix) headers = {} if auth_user is not None: auth_string = "{}:{}".format(auth_user, auth_password) b64_string = b64encode(auth_string.encode()).decode() auth_header = 'Basic {}'.format(b64_string) headers['Authorization'] = auth_header if content_type is not None: headers['Content-Type'] = content_type try: if data is not None: result = requests.post(url, headers=headers, data=data) else: result = requests.get(url, headers=headers) if result.status_code == 404: raise XoException("No such game: {}".format(name)) elif not result.ok: raise XoException("Error {}: {}".format( result.status_code, result.reason)) except requests.ConnectionError as err: raise XoException( 'Failed to connect to {}: {}'.format(url, str(err))) except BaseException as err: raise XoException(err) return result.text def _send_xo_txn(self, name, action, space="", wait=None, auth_user=None, auth_password=None): # Serialization is just a delimited utf-8 encoded string payload = ",".join([name, action, str(space)]).encode() # Construct the address address = self._get_address(name) header = TransactionHeader( signer_public_key=self._signer.get_public_key().as_hex(), family_name="xo", family_version="1.0", inputs=[address], outputs=[address], dependencies=[], payload_sha512=_sha512(payload), batcher_public_key=self._signer.get_public_key().as_hex(), nonce=time.time().hex().encode() ).SerializeToString() signature = self._signer.sign(header) transaction = Transaction( header=header, payload=payload, header_signature=signature ) batch_list = self._create_batch_list([transaction]) batch_id = batch_list.batches[0].header_signature if wait and wait > 0: wait_time = 0 start_time = time.time() response = self._send_request( "batches", batch_list.SerializeToString(), 'application/octet-stream', auth_user=auth_user, auth_password=auth_password) while wait_time < wait: status = self._get_status( batch_id, wait - int(wait_time), auth_user=auth_user, auth_password=auth_password) wait_time = time.time() - start_time if status != 'PENDING': return response return response return self._send_request( "batches", batch_list.SerializeToString(), 'application/octet-stream', auth_user=auth_user, auth_password=auth_password) def _create_batch_list(self, transactions): transaction_signatures = [t.header_signature for t in transactions] header = BatchHeader( signer_public_key=self._signer.get_public_key().as_hex(), transaction_ids=transaction_signatures ).SerializeToString() signature = self._signer.sign(header) batch = Batch( header=header, transactions=transactions, header_signature=signature) return BatchList(batches=[batch])
def test_verification(benchmark): context = create_context("secp256k1") factory = CryptoFactory(context) pub_key1 = Secp256k1PublicKey.from_hex(KEY1_PUB_HEX) result = benchmark(context.verify, MSG1_KEY1_SIG, MSG1.encode(), pub_key1) assert result == True
def test_block_publisher_doesnt_claim_readiness( self, mock_utils, mock_validator_registry_view, mock_consensus_state, mock_poet_enclave_factory, mock_consensus_state_store, mock_poet_key_state_store, mock_signup_info, mock_wait_time, mock_poet_settings_view, mock_block_wrapper): """ Test verifies that PoET Block Publisher doesn't claims readiness if the wait timer hasn't expired """ # create a mock_validator_registry_view with # get_validator_info that does nothing mock_validator_registry_view.return_value.get_validator_info. \ return_value = \ ValidatorInfo( name='validator_001', id='validator_deadbeef', signup_info=SignUpInfo( poet_public_key='00112233445566778899aabbccddeeff')) # create a mock_consensus_state that returns a mock with # the following settings: mock_state = MockConsensusState.create_mock_consensus_state() mock_consensus_state.consensus_state_for_block_id.return_value = \ mock_state mock_consensus_state_store.return_value.__getitem__.return_value = \ mock_consensus_state # Create mock key state mock_poet_key_state_store.return_value.__getitem__.return_value = \ mock.Mock( sealed_signup_data='sealed signup data', has_been_refreshed=False) # create mock_signup_info mock_signup_info.unseal_signup_data.return_value = \ '00112233445566778899aabbccddeeff' # create mock_batch_publisher context = create_context('secp256k1') private_key = context.new_random_private_key() crypto_factory = CryptoFactory(context) signer = crypto_factory.new_signer(private_key) mock_batch_publisher = mock.Mock(identity_signer=signer) mock_block_cache = mock.MagicMock() mock_state_view_factory = mock.Mock() # create mock_block_header with the following fields mock_block = mock.Mock(identifier='0123456789abcdefedcba9876543210') mock_block.header.signer_public_key = \ '90834587139405781349807435098745' mock_block.header.previous_block_id = '2' mock_block.header.block_num = 1 mock_block.header.state_root_hash = '6' mock_block.header.batch_ids = '4' # create a mock_wait_timer that hasn't expired yet my_wait_time = mock.Mock() my_wait_time.has_expired.return_value = False mock_wait_time.create_wait_timer.return_value = my_wait_time # create mock_poet_enclave_module mock_poet_enclave_module = mock.Mock() mock_poet_enclave_module.return_value = \ mock_poet_enclave_factory.get_poet_enclave_module.return_value # check test block_publisher = \ poet_block_publisher.PoetBlockPublisher( block_cache=mock_block_cache, state_view_factory=mock_state_view_factory, batch_publisher=mock_batch_publisher, data_dir=self._temp_dir, config_dir=self._temp_dir, validator_id='validator_deadbeef') # check initialize_block() first to set wait_timer self.assertTrue( block_publisher.initialize_block( block_header=mock_block.header)) # check that block_publisher only claims readiness # when the wait_timer has expired self.assertFalse( block_publisher.check_publish_block( block_header=mock_block.header))
def test_signup_info_not_committed_within_allowed_delay( self, mock_utils, mock_validator_registry_view, mock_consensus_state, mock_poet_enclave_factory, mock_consensus_state_store, mock_poet_key_state_store, mock_signup_info, mock_poet_settings_view, mock_block_wrapper): """ Test verifies that PoET Block Publisher fails if a validator's signup info was not committed to the block chain within the allowed configured delay """ # create a mock_validator_registry_view with # get_validator_info that does nothing mock_validator_registry_view.return_value.get_validator_info. \ return_value = \ ValidatorInfo( name='validator_001', id='validator_deadbeef', signup_info=SignUpInfo( poet_public_key='00112233445566778899aabbccddeeff', nonce='nonce')) # create a mock_wait_certificate that does nothing in check_valid mock_wait_certificate = mock.Mock() mock_wait_certificate.check_valid.return_value = None mock_utils.deserialize_wait_certificate.return_value = \ mock_wait_certificate # create a mock_consensus_state that returns a mock with # the following settings: mock_state = MockConsensusState.create_mock_consensus_state( committed_too_late=True) mock_consensus_state.consensus_state_for_block_id.return_value = \ mock_state mock_consensus_state_store.return_value.__getitem__.return_value = \ mock_consensus_state # Create mock key state mock_poet_key_state_store.return_value.__getitem__.return_value = \ mock.Mock( sealed_signup_data='sealed signup data', has_been_refreshed=False) # create mock_signup_info mock_signup_info.create_signup_info.return_value = \ mock.Mock( poet_public_key='poet public key', proof_data='proof data', anti_sybil_id='anti-sybil ID', sealed_signup_data='sealed signup data') mock_signup_info.block_id_to_nonce.return_value = 'nonce' mock_signup_info.unseal_signup_data.return_value = \ '00112233445566778899aabbccddeeff' # create mock_batch_publisher context = create_context('secp256k1') private_key = context.new_random_private_key() crypto_factory = CryptoFactory(context) signer = crypto_factory.new_signer(private_key) mock_batch_publisher = mock.Mock(identity_signer=signer) mock_block_cache = mock.MagicMock() mock_state_view_factory = mock.Mock() # create mock_block_header with the following fields mock_block = mock.Mock(identifier='0123456789abcdefedcba9876543210') mock_block.header.signer_public_key = \ '90834587139405781349807435098745' mock_block.header.previous_block_id = '2' mock_block.header.block_num = 1 mock_block.header.state_root_hash = '6' mock_block.header.batch_ids = '4' # check test with mock.patch('sawtooth_poet.poet_consensus.poet_block_publisher.' 'LOGGER') as mock_logger: block_publisher = \ poet_block_publisher.PoetBlockPublisher( block_cache=mock_block_cache, state_view_factory=mock_state_view_factory, batch_publisher=mock_batch_publisher, data_dir=self._temp_dir, config_dir=self._temp_dir, validator_id='validator_deadbeef') self.assertFalse( block_publisher.initialize_block( block_header=mock_block.header)) # Could be a hack, but verify that the appropriate log message is # generated - so we at least have some faith that the failure was # because of what we are testing and not something else. I know # that this is fragile if the log message is changed, so would # accept any suggestions on a better way to verify that the # function fails for the reason we expect. self.assertTrue( any('Validator signup information not committed in a timely ' 'manner.' in call[0][0] for call in mock_logger.info.call_args_list)) # check that create.signup_info() was called to create # the validator registry payload with new set of keys self.assertTrue(mock_signup_info.create_signup_info.called)