def test_validate_tx_threshold_duplicated_pk(user_pub, user_priv, asset_definition): from cryptoconditions import Ed25519Sha256, ThresholdSha256 from bigchaindb.common.transaction import Input, Output, Transaction threshold = ThresholdSha256(threshold=2) threshold.add_subfulfillment(Ed25519Sha256(public_key=b58decode(user_pub))) threshold.add_subfulfillment(Ed25519Sha256(public_key=b58decode(user_pub))) threshold_input = Input(threshold, [user_pub, user_pub]) threshold_output = Output(threshold, [user_pub, user_pub]) tx = Transaction(Transaction.CREATE, asset_definition, [threshold_input], [threshold_output]) expected = deepcopy(threshold_input) expected.fulfillment.subconditions[0]['body'].sign( str(tx).encode(), b58decode(user_priv)) expected.fulfillment.subconditions[1]['body'].sign( str(tx).encode(), b58decode(user_priv)) tx.sign([user_priv, user_priv]) subconditions = tx.inputs[0].fulfillment.subconditions expected_subconditions = expected.fulfillment.subconditions assert subconditions[0]['body'].to_dict()['signature'] == \ expected_subconditions[0]['body'].to_dict()['signature'] assert subconditions[1]['body'].to_dict()['signature'] == \ expected_subconditions[1]['body'].to_dict()['signature'] assert tx.inputs[0].to_dict()['fulfillment'] == \ expected.fulfillment.serialize_uri() assert tx.inputs_valid() is True
def test_fulfillment_nested_and_or(self, fulfillment_sha256, fulfillment_ed25519, fulfillment_threshold_nested_and_or): ilp_fulfillment_sha = Fulfillment.from_uri( fulfillment_sha256['fulfillment_uri']) ilp_fulfillment_ed = Fulfillment.from_uri( fulfillment_ed25519['fulfillment_uri']) # 2-of-2 (AND with 2 inputs) fulfillment = ThresholdSha256(threshold=2) fulfillment.add_subfulfillment(ilp_fulfillment_sha) assert fulfillment.validate(MESSAGE) is False # 1-of-2 (OR with 2 inputs) nested_fulfillment = ThresholdSha256(threshold=1) nested_fulfillment.add_subfulfillment(ilp_fulfillment_ed) assert nested_fulfillment.validate(MESSAGE) is True nested_fulfillment.add_subfulfillment(ilp_fulfillment_ed) assert nested_fulfillment.validate(MESSAGE) is True fulfillment.add_subfulfillment(nested_fulfillment) assert fulfillment.validate(MESSAGE) is True fulfillment_uri = fulfillment.serialize_uri() assert fulfillment.condition_uri == fulfillment_threshold_nested_and_or[ 'condition_uri'] assert fulfillment_uri == fulfillment_threshold_nested_and_or[ 'fulfillment_uri'] print(fulfillment_uri) deserialized_fulfillment = Fulfillment.from_uri(fulfillment_uri) condition_uri = fulfillment.condition.serialize_uri() deserialized_condition = Condition.from_uri(condition_uri) assert isinstance(deserialized_fulfillment, ThresholdSha256) assert deserialized_fulfillment.threshold == 2 assert len(deserialized_fulfillment.subconditions) == 2 assert len(deserialized_fulfillment.subconditions[1] ['body'].subconditions) == 2 assert deserialized_fulfillment.serialize_uri() == fulfillment_uri assert deserialized_fulfillment.validate(MESSAGE) assert deserialized_condition.serialize_uri() == condition_uri vk = ilp_fulfillment_ed.public_key.encode(encoding='base58') assert len(fulfillment.get_subcondition_from_vk(vk)) == 2 assert len(deserialized_fulfillment.get_subcondition_from_vk(vk)) == 1
def test_generate_output_split_half_recursive(user_pub, user2_pub, user3_pub): from bigchaindb.common.transaction import Output from cryptoconditions import Ed25519Sha256, ThresholdSha256 expected_simple1 = Ed25519Sha256(public_key=b58decode(user_pub)) expected_simple2 = Ed25519Sha256(public_key=b58decode(user2_pub)) expected_simple3 = Ed25519Sha256(public_key=b58decode(user3_pub)) expected = ThresholdSha256(threshold=2) expected.add_subfulfillment(expected_simple1) expected_threshold = ThresholdSha256(threshold=2) expected_threshold.add_subfulfillment(expected_simple2) expected_threshold.add_subfulfillment(expected_simple3) expected.add_subfulfillment(expected_threshold) cond = Output.generate([user_pub, [user2_pub, expected_simple3]], 1) assert cond.fulfillment.to_dict() == expected.to_dict()
def user_user2_threshold(user_pub, user2_pub): from cryptoconditions import ThresholdSha256, Ed25519Sha256 user_pub_keys = [user_pub, user2_pub] threshold = ThresholdSha256(threshold=len(user_pub_keys)) for user_pub in user_pub_keys: threshold.add_subfulfillment( Ed25519Sha256(public_key=b58decode(user_pub))) return threshold
def add_nested_fulfillment(parent, current_depth=0): current_depth += 1 child = ThresholdSha256(threshold=1) if current_depth < max_depth: add_nested_fulfillment(child, current_depth) else: child.add_subfulfillment(ilp_fulfillment_ed1) parent.add_subfulfillment(child) return parent
def test_deserialize_unsigned_dict_to_fulfillment(self, vk_ilp): fulfillment = ThresholdSha256(threshold=1) fulfillment.add_subfulfillment( Ed25519Sha256(public_key=VerifyingKey(vk_ilp['b58']))) fulfillment.add_subfulfillment( Ed25519Sha256(public_key=VerifyingKey(vk_ilp['b58']))) parsed_fulfillment = fulfillment.from_dict(fulfillment.to_dict()) assert parsed_fulfillment.condition.serialize_uri( ) == fulfillment.condition.serialize_uri() assert parsed_fulfillment.to_dict() == fulfillment.to_dict()
def _gen_condition(cls, initial, new_public_keys): """Generates ThresholdSha256 conditions from a list of new owners. Note: This method is intended only to be used with a reduce function. For a description on how to use this method, see :meth:`~.Output.generate`. Args: initial (:class:`cryptoconditions.ThresholdSha256`): A Condition representing the overall root. new_public_keys (:obj:`list` of :obj:`str`|str): A list of new owners or a single new owner. Returns: :class:`cryptoconditions.ThresholdSha256`: """ try: threshold = len(new_public_keys) except TypeError: threshold = None if isinstance(new_public_keys, list) and len(new_public_keys) > 1: ffill = ThresholdSha256(threshold=threshold) reduce(cls._gen_condition, new_public_keys, ffill) elif isinstance(new_public_keys, list) and len(new_public_keys) <= 1: raise ValueError('Sublist cannot contain single owner') else: try: new_public_keys = new_public_keys.pop() except AttributeError: pass # NOTE: Instead of submitting base58 encoded addresses, a user # of this class can also submit fully instantiated # Cryptoconditions. In the case of casting # `new_public_keys` to a Ed25519Fulfillment with the # result of a `TypeError`, we're assuming that # `new_public_keys` is a Cryptocondition then. if isinstance(new_public_keys, Fulfillment): ffill = new_public_keys else: ffill = Ed25519Sha256( public_key=base58.b58decode(new_public_keys)) initial.add_subfulfillment(ffill) return initial
def generate(cls, public_keys, amount): """Generates a Output from a specifically formed tuple or list. Note: If a ThresholdCondition has to be generated where the threshold is always the number of subconditions it is split between, a list of the following structure is sufficient: [(address|condition)*, [(address|condition)*, ...], ...] Args: public_keys (:obj:`list` of :obj:`str`): The public key of the users that should be able to fulfill the Condition that is being created. amount (:obj:`int`): The amount locked by the Output. Returns: An Output that can be used in a Transaction. Raises: TypeError: If `public_keys` is not an instance of `list`. ValueError: If `public_keys` is an empty list. """ threshold = len(public_keys) if not isinstance(amount, int): raise TypeError('`amount` must be a int') if amount < 1: raise AmountError('`amount` needs to be greater than zero') if not isinstance(public_keys, list): raise TypeError('`public_keys` must be an instance of list') if len(public_keys) == 0: raise ValueError('`public_keys` needs to contain at least one' 'owner') elif len(public_keys) == 1 and not isinstance(public_keys[0], list): if isinstance(public_keys[0], Fulfillment): ffill = public_keys[0] else: ffill = Ed25519Sha256( public_key=base58.b58decode(public_keys[0])) return cls(ffill, public_keys, amount=amount) else: initial_cond = ThresholdSha256(threshold=threshold) threshold_cond = reduce(cls._gen_condition, public_keys, initial_cond) return cls(threshold_cond, public_keys, amount=amount)
def test_fulfillment_nested( self, fulfillment_sha256, fulfillment_ed25519_2, ): ilp_fulfillment_sha = Fulfillment.from_uri( fulfillment_sha256['fulfillment_uri']) ilp_fulfillment_ed1 = Fulfillment.from_uri( fulfillment_ed25519_2['fulfillment_uri']) # 2-of-2 (AND with 2 inputs) fulfillment = ThresholdSha256(threshold=2) fulfillment.add_subfulfillment(ilp_fulfillment_sha) max_depth = 6 def add_nested_fulfillment(parent, current_depth=0): current_depth += 1 child = ThresholdSha256(threshold=1) if current_depth < max_depth: add_nested_fulfillment(child, current_depth) else: child.add_subfulfillment(ilp_fulfillment_ed1) parent.add_subfulfillment(child) return parent fulfillment = add_nested_fulfillment(fulfillment) assert fulfillment.validate(MESSAGE) is True assert len(fulfillment.subconditions) == 2 assert isinstance(fulfillment.subconditions[1]['body'], ThresholdSha256) assert isinstance( fulfillment.subconditions[1]['body'].subconditions[0]['body'], ThresholdSha256) fulfillment_uri = fulfillment.serialize_uri() deserialized_fulfillment = Fulfillment.from_uri(fulfillment_uri) condition_uri = fulfillment.condition.serialize_uri() deserialized_condition = Condition.from_uri(condition_uri) assert deserialized_fulfillment.serialize_uri() == fulfillment_uri assert deserialized_fulfillment.validate(MESSAGE) is True assert deserialized_condition.serialize_uri() == condition_uri
def sign_service(message, owner_priv_keys): """ finalize the generated service message by adding condition attributes Args: message(dict) - a dictionary representing an unsigned attribute. owner_priv_keys([str]) - list of owner base58 private keys that will be used to create the conditions fulfillments([fulfillments]) -list of fulfillments needed to be fulfilled by the private keys returns : a message with fulfilled conditions representing a valid creation transaction raises: Invalid public private key pairing Insufficient private keys """ fulfillment = None jsonMessage = json.dumps( message, sort_keys=True, separators=(',', ':'), ensure_ascii=False, ) encoded_message = sha3.sha3_256(jsonMessage.encode()) message['data']['fulfillment'] = {} if len(owner_priv_keys) == 1: fulfillment = ThresholdSha256(threshold=len(owner_priv_keys)) message['data']['fulfillment']['type'] = ThresholdSha256.TYPE_NAME for key in owner_priv_keys: f = Ed25519Sha256() f.sign(encoded_message.digest(), base58.b58decode(key)) fulfillment.add_subfulfillment(f) else: fulfillment = Ed25519Sha256() fulfillment.sign(encoded_message.digest(), base58.b58decode(owner_priv_keys[0])) message['data']['fulfillment']['type'] = Ed25519Sha256.TYPE_NAME message['data']['fulfillment'][ 'fulfillment_uri'] = fulfillment.serialize_uri() message['id'] = encoded_message.hexdigest() return message
def test_validate_tx_threshold_duplicated_pk(user_pub, user_priv, asset_definition): from cryptoconditions import Ed25519Sha256, ThresholdSha256 from bigchaindb.common.transaction import Input, Output, Transaction threshold = ThresholdSha256(threshold=2) threshold.add_subfulfillment(Ed25519Sha256(public_key=b58decode(user_pub))) threshold.add_subfulfillment(Ed25519Sha256(public_key=b58decode(user_pub))) threshold_input = Input(threshold, [user_pub, user_pub]) threshold_output = Output(threshold, [user_pub, user_pub]) tx = Transaction(Transaction.CREATE, asset_definition, [threshold_input], [threshold_output]) tx_dict = tx.to_dict() tx_dict['inputs'][0]['fulfillment'] = None serialized_tx = json.dumps(tx_dict, sort_keys=True, separators=(',', ':'), ensure_ascii=True) message = sha3_256(serialized_tx.encode()).digest() expected = deepcopy(threshold_input) expected.fulfillment.subconditions[0]['body'].sign(message, b58decode(user_priv)) expected.fulfillment.subconditions[1]['body'].sign(message, b58decode(user_priv)) tx.sign([user_priv, user_priv]) subconditions = tx.inputs[0].fulfillment.subconditions expected_subconditions = expected.fulfillment.subconditions assert subconditions[0]['body'].to_dict()['signature'] == \ expected_subconditions[0]['body'].to_dict()['signature'] assert subconditions[1]['body'].to_dict()['signature'] == \ expected_subconditions[1]['body'].to_dict()['signature'] assert tx.inputs[0].to_dict()['fulfillment'] == \ expected.fulfillment.serialize_uri() assert tx.inputs_valid() is True
def _fulfillment_from_details(data, _depth=0): """Load a fulfillment for a signing spec dictionary Args: data: tx.output[].condition.details dictionary """ if _depth == 100: raise ThresholdTooDeep() if data['type'] == 'ed25519-sha-256': public_key = base58.b58decode(data['public_key']) return Ed25519Sha256(public_key=public_key) if data['type'] == 'threshold-sha-256': threshold = ThresholdSha256(data['threshold']) for cond in data['subconditions']: cond = _fulfillment_from_details(cond, _depth+1) threshold.add_subfulfillment(cond) return threshold raise UnsupportedTypeError(data.get('type'))
def generate_service(service_key, owner_keys, auth_keys=[], owners_authorize=True): """ create a an unsigned service msg Args: service_keys ([str])- service denoted by base58 encoded keys owner_keys ([str]) - public keys recognizing ownership of the services and therefore administrators auth_keys ([str])- those who can give access to a resource. Generally owners must also be authorizers returns: a message without any signature or identity resolution """ message = MessageShell.messageForType(MSGTYPE.REGISTER) message['data']['service'] = service_key message['data']['owners'] = owner_keys if owners_authorize: message['data']['authorities'] = owner_keys + auth_keys else: message['data']['authorities'] = auth_keys condition = None if len(owner_keys) != 1: condition = ThresholdSha256(threshold=len(owner_keys)) for key in owner_keys: condition.add_subfulfillment( Ed25519Sha256(public_key=base58.b58decode(key))) else: condition = Ed25519Sha256(public_key=base58.b58decode(owner_keys[0])) message['data']['condition'] = condition.to_dict() message['data']['condition_uri'] = condition.condition_uri message['data']['fulfillment'] = None message['id'] = None return message
def test_fulfillment_didnt_reach_threshold(self, vk_ilp, fulfillment_ed25519): ilp_fulfillment = Fulfillment.from_uri( fulfillment_ed25519['fulfillment_uri']) threshold = 10 # Create a threshold condition fulfillment = ThresholdSha256(threshold=threshold) for i in range(threshold - 1): fulfillment.add_subfulfillment(ilp_fulfillment) with pytest.raises(KeyError): fulfillment.serialize_uri() assert fulfillment.validate(MESSAGE) is False fulfillment.add_subfulfillment(ilp_fulfillment) fulfillment_uri = fulfillment.serialize_uri() assert fulfillment.validate(MESSAGE) deserialized_fulfillment = Fulfillment.from_uri(fulfillment_uri) assert isinstance(deserialized_fulfillment, ThresholdSha256) assert deserialized_fulfillment.threshold == threshold assert len([ f for f in deserialized_fulfillment.subconditions if f['type'] == 'fulfillment' ]) == threshold assert len(deserialized_fulfillment.subconditions) == threshold assert deserialized_fulfillment.serialize_uri() == fulfillment_uri assert deserialized_fulfillment.validate(MESSAGE) fulfillment.add_subfulfillment( Ed25519Sha256(public_key=VerifyingKey(vk_ilp['b58']))) assert fulfillment.validate(MESSAGE) == True
def test_serialize_unsigned_dict_to_fulfillment(self, vk_ilp): fulfillment = ThresholdSha256(threshold=1) fulfillment.add_subfulfillment( Ed25519Sha256(public_key=VerifyingKey(vk_ilp['b58']))) fulfillment.add_subfulfillment( Ed25519Sha256(public_key=VerifyingKey(vk_ilp['b58']))) assert fulfillment.to_dict() == \ {'bitmask': 41, 'subfulfillments': [{'bitmask': 32, 'public_key': 'Gtbi6WQDB6wUePiZm8aYs5XZ5pUqx9jMMLvRVHPESTjU', 'signature': None, 'type': 'fulfillment', 'type_id': 4, 'weight': 1}, {'bitmask': 32, 'public_key': 'Gtbi6WQDB6wUePiZm8aYs5XZ5pUqx9jMMLvRVHPESTjU', 'signature': None, 'type': 'fulfillment', 'type_id': 4, 'weight': 1}], 'threshold': 1, 'type': 'fulfillment', 'type_id': 2}
def test_permission_add_role(b): from .utils import create_simple_tx, post_tx, transfer_simple_tx, prepare_transfer, get_message_to_sign from bigchaindb.models import Transaction from cryptoconditions import ThresholdSha256, Ed25519Sha256, PreimageSha256 from cryptoconditions.crypto import Ed25519VerifyingKey, Ed25519SigningKey # admin, albi, bruce admin_priv, admin_pub = crypto.generate_key_pair() albi_priv, albi_pub = crypto.generate_key_pair() bruce_priv, bruce_pub = crypto.generate_key_pair() assert len(b.get_owned_ids(admin_pub)) == 0 assert len(b.get_owned_ids(albi_pub)) == 0 assert len(b.get_owned_ids(bruce_pub)) == 0 # admin CREATES role: tx_create_role, 202 tx_create_role = Transaction.create( [admin_pub], [([admin_pub], 1)], asset={ "policy": [ { "condition": "transaction.operation == 'TRANSFER'", "rule": "transaction.inputs[0].owners_before[0] == '{}'".format( admin_pub) }, ] }) # trick to not include in balance part 1 tx_create_role.outputs[0].public_keys = [] tx_create_role = tx_create_role.sign([admin_priv]) response = post_tx(b, None, tx_create_role) tx_create_role_retrieved = b.get_transaction(tx_create_role.id) assert response.status_code == 202 assert tx_create_role_retrieved.id == tx_create_role.id # admin.unspents = [] # admin doesnt have role, only created it # user_a.unspents = [] # user_a has no role # user_b.unspents = [] # user_b has no role assert len(b.get_owned_ids(admin_pub)) == 0 assert len(b.get_owned_ids(albi_pub)) == 0 assert len(b.get_owned_ids(bruce_pub)) == 0 # admin TRANSFERS tx_create_role TO user_a: tx_transfer_role_a, 202 # tx_transfer_role_a = Transaction.transfer( # [ # Input( # fulfillment=Ed25519Sha256( # public_key=b58decode(admin_pub)), # owners_before=[admin_pub], # trick to not include in balance part 2 # fulfills=TransactionLink( # txid=tx_create_role.id, # output=0) # ) # ], # [([albi_pub], 1)], # tx_create_role.id) # tx_transfer_role_a = tx_transfer_role_a.sign([admin_priv]) output_condition = ThresholdSha256(threshold=1) output_condition.add_subfulfillment( Ed25519Sha256(public_key=b58decode(admin_pub))) output_condition.add_subfulfillment( Ed25519Sha256(public_key=b58decode(albi_pub))) tx_transfer_role_a = prepare_transfer(inputs=[{ 'tx': tx_create_role.to_dict(), 'output': 0 }], outputs=[ { 'condition': output_condition, 'public_keys': [albi_pub] }, ]) input_fulfillment = Ed25519Sha256(public_key=b58decode(admin_pub)) tx_transfer_role_a.inputs[0].owners_before = [admin_pub] message_to_sign = get_message_to_sign(tx_transfer_role_a) input_fulfillment.sign(message_to_sign, b58decode(admin_priv)) tx_transfer_role_a.inputs[0].fulfillment = input_fulfillment tx_transfer_role_a.validate(b) response = post_tx(b, None, tx_transfer_role_a) # tx_create_role_retrieved = b.get_transaction(tx_create_role.id) assert response.status_code == 202 # user_a.unspents = [tx_transfer_role_a] # user_a has role assert len(b.get_owned_ids(admin_pub)) == 0 assert len(b.get_owned_ids(albi_pub)) == 1 assert len(b.get_owned_ids(bruce_pub)) == 0 # user TRANSFERS tx_transfer_role_a TO user_b: -, 400 # only admin can assign role tx_transfer_role_b = prepare_transfer( inputs=[{ 'tx': tx_transfer_role_a.to_dict(), 'output': 0 }], outputs=[ { 'condition': Ed25519Sha256(public_key=b58decode(bruce_pub)), }, ]) tx_transfer_role_b.inputs[0].owners_before = [albi_pub] message_to_sign = get_message_to_sign(tx_transfer_role_b) input_fulfillment = ThresholdSha256(threshold=1) albi_fulfillment = Ed25519Sha256(public_key=b58decode(albi_pub)) albi_fulfillment.sign(message_to_sign, b58decode(albi_priv)) input_fulfillment.add_subfulfillment(albi_fulfillment) input_fulfillment.add_subcondition( Ed25519Sha256(public_key=b58decode(admin_pub)).condition_uri) tx_transfer_role_b.inputs[0].fulfillment = input_fulfillment input_fulfillment.serialize_uri() tx_transfer_role_b.validate(b) response = post_tx(b, None, tx_transfer_role_b) assert response.status_code == 202 # user_a.unspents = [tx_transfer_role_a] # user_a has role # user_b.unspents = [] # user_b has no role assert len(b.get_owned_ids(admin_pub)) == 0 assert len(b.get_owned_ids(albi_pub)) == 0 assert len(b.get_owned_ids(bruce_pub)) == 0