def test_escrow_abort(self, fulfillment_sha256, fulfillment_ed25519): ilp_fulfillment_sha = Fulfillment.from_uri( fulfillment_sha256['fulfillment_uri']) ilp_fulfillment_ed = Fulfillment.from_uri( fulfillment_ed25519['fulfillment_uri']) time_sleep = 0 fulfillment_escrow = ThresholdSha256Fulfillment(threshold=1) fulfillment_timeout = TimeoutFulfillment( expire_time=str(float(timestamp()) + time_sleep)) fulfillment_timeout_inverted = InvertedThresholdSha256Fulfillment( threshold=1) fulfillment_timeout_inverted.add_subfulfillment(fulfillment_timeout) # do not fulfill execute branch fulfillment_and_execute = ThresholdSha256Fulfillment(threshold=2) fulfillment_and_execute.add_subcondition(ilp_fulfillment_ed.condition) fulfillment_and_execute.add_subfulfillment(fulfillment_timeout) fulfillment_and_abort = ThresholdSha256Fulfillment(threshold=2) fulfillment_and_abort.add_subfulfillment(ilp_fulfillment_sha) fulfillment_and_abort.add_subfulfillment(fulfillment_timeout_inverted) fulfillment_escrow.add_subfulfillment(fulfillment_and_execute) fulfillment_escrow.add_subfulfillment(fulfillment_and_abort) # out-of-time validation assert fulfillment_escrow.validate(MESSAGE, now=timestamp()) is True
def test_weights(self, fulfillment_ed25519): ilp_fulfillment = Fulfillment.from_uri( fulfillment_ed25519['fulfillment_uri']) fulfillment1 = ThresholdSha256Fulfillment(threshold=2) fulfillment1.add_subfulfillment(ilp_fulfillment, weight=2) parsed_fulfillment1 = fulfillment1.from_dict(fulfillment1.to_dict()) assert parsed_fulfillment1.condition.serialize_uri( ) == fulfillment1.condition.serialize_uri() assert parsed_fulfillment1.to_dict() == fulfillment1.to_dict() assert parsed_fulfillment1.subconditions[0]['weight'] == 2 assert parsed_fulfillment1.validate(MESSAGE) is True fulfillment2 = ThresholdSha256Fulfillment(threshold=3) fulfillment2.add_subfulfillment(ilp_fulfillment, weight=2) parsed_fulfillment2 = fulfillment1.from_dict(fulfillment2.to_dict()) assert parsed_fulfillment2.subconditions[0]['weight'] == 2 assert parsed_fulfillment2.validate(MESSAGE) is False fulfillment3 = ThresholdSha256Fulfillment(threshold=3) fulfillment3.add_subfulfillment(ilp_fulfillment, weight=3) parsed_fulfillment3 = fulfillment1.from_dict(fulfillment3.to_dict()) assert parsed_fulfillment3.condition.serialize_uri( ) == fulfillment3.condition.serialize_uri() assert not (fulfillment3.condition.serialize_uri() == fulfillment1.condition.serialize_uri()) assert parsed_fulfillment3.validate(MESSAGE) is True fulfillment4 = ThresholdSha256Fulfillment(threshold=2) with pytest.raises(ValueError): fulfillment4.add_subfulfillment(ilp_fulfillment, weight=-2)
def test_serialize_condition_and_validate_fulfillment( self, fulfillment_sha256, fulfillment_ed25519, fulfillment_threshold): ilp_fulfillment_ed25519 = Fulfillment.from_uri( fulfillment_ed25519['fulfillment_uri']) ilp_fulfillment_sha = Fulfillment.from_uri( fulfillment_sha256['fulfillment_uri']) assert ilp_fulfillment_ed25519.validate(MESSAGE) == True assert ilp_fulfillment_sha.validate(MESSAGE) == True threshold = 1 # Create a threshold condition fulfillment = ThresholdSha256Fulfillment(threshold=threshold) fulfillment.add_subfulfillment(ilp_fulfillment_ed25519) fulfillment.add_subfulfillment(ilp_fulfillment_sha) assert fulfillment.condition.serialize_uri( ) == fulfillment_threshold['condition_uri'] # Note: If there are more than enough fulfilled subconditions, shorter # fulfillments will be chosen over longer ones. # thresholdFulfillmentUri.length === 65 assert fulfillment.serialize_uri( ) == fulfillment_threshold['fulfillment_uri'] assert fulfillment.validate(MESSAGE)
def create_asset(vw_sk, vw_pk, vehicle_id, data, metadata): # Create asset VW -> [VW, TEL] # Custom crypto condition multisig 1-2 threshold_fulfillment = ThresholdSha256Fulfillment(threshold=1) vw_fulfillment = Ed25519Fulfillment(public_key=vw_pk) tel_fulfillment = Ed25519Fulfillment(public_key=vehicle_id) threshold_fulfillment.add_subfulfillment(vw_fulfillment) threshold_fulfillment.add_subfulfillment(tel_fulfillment) output = { 'amount': 1, 'condition': { 'details': threshold_fulfillment.to_dict(), 'uri': threshold_fulfillment.condition.serialize_uri() }, 'public_keys': [vw_pk, vehicle_id] } # Create the transaction tx_create = Transaction.create([vw_pk], [([vw_pk, vehicle_id], 1)], asset=data, metadata=metadata) # Override the condition we our custom build one tx_create.outputs[0] = Output.from_dict(output) # Sign the transaction tx_create_signed = tx_create.sign([vw_sk]) return tx_create_signed
def test_serialize_deserialize_fulfillment(self, fulfillment_ed25519): ilp_fulfillment = Fulfillment.from_uri( fulfillment_ed25519['fulfillment_uri']) num_fulfillments = 20 threshold = ceil(num_fulfillments * 2 / 3) # Create a threshold condition fulfillment = ThresholdSha256Fulfillment(threshold=threshold) for i in range(num_fulfillments): 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, ThresholdSha256Fulfillment) assert deserialized_fulfillment.threshold == threshold assert len([ f for f in deserialized_fulfillment.subconditions if f['type'] == 'fulfillment' ]) == threshold assert len(deserialized_fulfillment.subconditions) == num_fulfillments assert deserialized_fulfillment.serialize_uri() == fulfillment_uri assert deserialized_fulfillment.validate(MESSAGE)
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 = ThresholdSha256Fulfillment(threshold=2) fulfillment.add_subfulfillment(ilp_fulfillment_sha) assert fulfillment.validate(MESSAGE) is False # 1-of-2 (OR with 2 inputs) nested_fulfillment = ThresholdSha256Fulfillment(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, ThresholdSha256Fulfillment) 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 user_user2_threshold(user_pub, user2_pub): from cryptoconditions import (ThresholdSha256Fulfillment, Ed25519Fulfillment) user_pub_keys = [user_pub, user2_pub] threshold = ThresholdSha256Fulfillment(threshold=len(user_pub_keys)) for user_pub in user_pub_keys: threshold.add_subfulfillment(Ed25519Fulfillment(public_key=user_pub)) return threshold
def add_nested_fulfillment(parent, current_depth=0): current_depth += 1 child = ThresholdSha256Fulfillment(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_generate_conditions_split_half_single_owner(user_pub, user2_pub, user3_pub): from bigchaindb_common.transaction import Condition from cryptoconditions import Ed25519Fulfillment, ThresholdSha256Fulfillment expected_simple1 = Ed25519Fulfillment(public_key=user_pub) expected_simple2 = Ed25519Fulfillment(public_key=user2_pub) expected_simple3 = Ed25519Fulfillment(public_key=user3_pub) expected = ThresholdSha256Fulfillment(threshold=2) expected_threshold = ThresholdSha256Fulfillment(threshold=2) expected_threshold.add_subfulfillment(expected_simple2) expected_threshold.add_subfulfillment(expected_simple3) expected.add_subfulfillment(expected_threshold) expected.add_subfulfillment(expected_simple1) cond = Condition.generate([[expected_simple2, user3_pub], user_pub]) assert cond.fulfillment.to_dict() == expected.to_dict()
def test_deserialize_unsigned_dict_to_fulfillment(self, vk_ilp): fulfillment = ThresholdSha256Fulfillment(threshold=1) fulfillment.add_subfulfillment( Ed25519Fulfillment(public_key=VerifyingKey(vk_ilp['b58']))) fulfillment.add_subfulfillment( Ed25519Fulfillment(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 test_generate_conditions_split_half_recursive_custom_threshold(user_pub, user2_pub, user3_pub): from bigchaindb_common.transaction import Condition from cryptoconditions import Ed25519Fulfillment, ThresholdSha256Fulfillment expected_simple1 = Ed25519Fulfillment(public_key=user_pub) expected_simple2 = Ed25519Fulfillment(public_key=user2_pub) expected_simple3 = Ed25519Fulfillment(public_key=user3_pub) expected = ThresholdSha256Fulfillment(threshold=1) expected.add_subfulfillment(expected_simple1) expected_threshold = ThresholdSha256Fulfillment(threshold=1) expected_threshold.add_subfulfillment(expected_simple2) expected_threshold.add_subfulfillment(expected_simple3) expected.add_subfulfillment(expected_threshold) cond = Condition.generate(([user_pub, ([user2_pub, expected_simple3], 1)], 1)) assert cond.fulfillment.to_dict() == expected.to_dict()
def generate(cls, owners_after): """Generates a Condition 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)*, ...], ...] If however, the thresholds of individual threshold conditions to be created have to be set specifically, a tuple of the following structure is necessary: ([(address|condition)*, ([(address|condition)*, ...], subthreshold), ...], threshold) Args: owners_after (:obj:`list` of :obj:`str`|tuple): The users that should be able to fulfill the Condition that is being created. Returns: A Condition that can be used in a Transaction. Returns: TypeError: If `owners_after` is not an instance of `list`. TypeError: If `owners_after` is an empty list. """ # TODO: We probably want to remove the tuple logic for weights here # again: # github.com/bigchaindb/bigchaindb/issues/730#issuecomment-255144756 if isinstance(owners_after, tuple): owners_after, threshold = owners_after else: threshold = len(owners_after) if not isinstance(owners_after, list): raise TypeError('`owners_after` must be an instance of list') if len(owners_after) == 0: raise ValueError('`owners_after` needs to contain at least one' 'owner') elif len(owners_after) == 1 and not isinstance(owners_after[0], list): try: ffill = Ed25519Fulfillment(public_key=owners_after[0]) except TypeError: ffill = owners_after[0] return cls(ffill, owners_after) else: initial_cond = ThresholdSha256Fulfillment(threshold=threshold) threshold_cond = reduce(cls._gen_condition, owners_after, initial_cond) return cls(threshold_cond, owners_after)
def test_generate_threshold_condition_with_hashlock(user_pub, user2_pub, user3_pub): from bigchaindb_common.transaction import Condition from cryptoconditions import (PreimageSha256Fulfillment, Ed25519Fulfillment, ThresholdSha256Fulfillment) secret = b'much secret, wow' hashlock = PreimageSha256Fulfillment(preimage=secret) expected_simple1 = Ed25519Fulfillment(public_key=user_pub) expected_simple3 = Ed25519Fulfillment(public_key=user3_pub) expected = ThresholdSha256Fulfillment(threshold=2) expected_sub = ThresholdSha256Fulfillment(threshold=2) expected_sub.add_subfulfillment(expected_simple1) expected_sub.add_subfulfillment(hashlock) expected.add_subfulfillment(expected_simple3) cond = Condition.generate([[user_pub, hashlock], expected_simple3]) assert cond.fulfillment.to_dict() == expected.to_dict()
def test_serialize_condition_and_validate_fulfillment( self, fulfillment_sha256, fulfillment_ed25519): ilp_fulfillment_sha = Fulfillment.from_uri( fulfillment_sha256['fulfillment_uri']) ilp_fulfillment_ed = Fulfillment.from_uri( fulfillment_ed25519['fulfillment_uri']) fulfillment_escrow = ThresholdSha256Fulfillment(threshold=1) fulfillment_timeout = TimeoutFulfillment( expire_time=str(float(timestamp()) + 1000)) fulfillment_timeout_inverted = InvertedThresholdSha256Fulfillment( threshold=1) fulfillment_timeout_inverted.add_subfulfillment(fulfillment_timeout) fulfillment_and_execute = ThresholdSha256Fulfillment(threshold=2) fulfillment_and_execute.add_subfulfillment(ilp_fulfillment_ed) fulfillment_and_execute.add_subfulfillment(fulfillment_timeout) assert fulfillment_and_execute.validate(MESSAGE, now=timestamp()) is True fulfillment_and_abort = ThresholdSha256Fulfillment(threshold=2) fulfillment_and_abort.add_subfulfillment(ilp_fulfillment_sha) fulfillment_and_abort.add_subfulfillment(fulfillment_timeout_inverted) # timeout has not occured (over about 1000 seconds) assert fulfillment_and_abort.validate(MESSAGE, now=timestamp()) is False fulfillment_escrow.add_subfulfillment(fulfillment_and_execute) fulfillment_escrow.add_subfulfillment(fulfillment_and_abort) parsed_fulfillment = fulfillment_escrow.from_dict( fulfillment_escrow.to_dict()) assert parsed_fulfillment.condition_uri == fulfillment_escrow.condition_uri assert parsed_fulfillment.serialize_uri( ) == fulfillment_escrow.serialize_uri() assert parsed_fulfillment.validate(MESSAGE, now=timestamp()) is True
def test_generate_conditions_flat_ownage(user_pub, user2_pub, user3_pub): from bigchaindb_common.transaction import Condition from cryptoconditions import Ed25519Fulfillment, ThresholdSha256Fulfillment expected_simple1 = Ed25519Fulfillment(public_key=user_pub) expected_simple2 = Ed25519Fulfillment(public_key=user2_pub) expected_simple3 = Ed25519Fulfillment(public_key=user3_pub) expected = ThresholdSha256Fulfillment(threshold=3) expected.add_subfulfillment(expected_simple1) expected.add_subfulfillment(expected_simple2) expected.add_subfulfillment(expected_simple3) cond = Condition.generate([user_pub, user2_pub, expected_simple3]) assert cond.fulfillment.to_dict() == expected.to_dict()
def _gen_condition(cls, initial, current): """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 `Condition.generate`. Args: initial (:class:`cryptoconditions.ThresholdSha256Fulfillment`): A Condition representing the overall root. current (:obj:`list` of :obj:`str`|str): A list of new owners or a single new owner. Returns: :class:`cryptoconditions.ThresholdSha256Fulfillment`: """ if isinstance(current, tuple): owners_after, threshold = current else: owners_after = current try: threshold = len(owners_after) except TypeError: threshold = None if isinstance(owners_after, list) and len(owners_after) > 1: ffill = ThresholdSha256Fulfillment(threshold=threshold) reduce(cls._gen_condition, owners_after, ffill) elif isinstance(owners_after, list) and len(owners_after) <= 1: raise ValueError('Sublist cannot contain single owner') else: try: owners_after = owners_after.pop() except AttributeError: pass try: ffill = Ed25519Fulfillment(public_key=owners_after) except TypeError: # NOTE: Instead of submitting base58 encoded addresses, a user # of this class can also submit fully instantiated # Cryptoconditions. In the case of casting `owners_after` # to a Ed25519Fulfillment with the result of a # `TypeError`, we're assuming that `owners_after` is a # Cryptocondition then. ffill = owners_after initial.add_subfulfillment(ffill) return initial
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 = ThresholdSha256Fulfillment(threshold=2) fulfillment.add_subfulfillment(ilp_fulfillment_sha) max_depth = 6 def add_nested_fulfillment(parent, current_depth=0): current_depth += 1 child = ThresholdSha256Fulfillment(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'], ThresholdSha256Fulfillment) assert isinstance( fulfillment.subconditions[1]['body'].subconditions[0]['body'], ThresholdSha256Fulfillment) 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 generate(cls, owners_after, amount): """Generates a Condition 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: owners_after (: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 condition. Returns: A Condition that can be used in a Transaction. Raises: TypeError: If `owners_after` is not an instance of `list`. ValueError: If `owners_after` is an empty list. """ threshold = len(owners_after) 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(owners_after, list): raise TypeError('`owners_after` must be an instance of list') if len(owners_after) == 0: raise ValueError('`owners_after` needs to contain at least one' 'owner') elif len(owners_after) == 1 and not isinstance(owners_after[0], list): try: ffill = Ed25519Fulfillment(public_key=owners_after[0]) except TypeError: ffill = owners_after[0] return cls(ffill, owners_after, amount=amount) else: initial_cond = ThresholdSha256Fulfillment(threshold=threshold) threshold_cond = reduce(cls._gen_condition, owners_after, initial_cond) return cls(threshold_cond, owners_after, amount=amount)
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.ThresholdSha256Fulfillment`): 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.ThresholdSha256Fulfillment`: """ try: threshold = len(new_public_keys) except TypeError: threshold = None if isinstance(new_public_keys, list) and len(new_public_keys) > 1: ffill = ThresholdSha256Fulfillment(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 try: ffill = Ed25519Fulfillment(public_key=new_public_keys) except TypeError: # 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. ffill = new_public_keys initial.add_subfulfillment(ffill) return initial
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 = ThresholdSha256Fulfillment(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, ThresholdSha256Fulfillment) 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( Ed25519Fulfillment(public_key=VerifyingKey(vk_ilp['b58']))) assert fulfillment.validate(MESSAGE) == True
def test_serialize_unsigned_dict_to_fulfillment(self, vk_ilp): fulfillment = ThresholdSha256Fulfillment(threshold=1) fulfillment.add_subfulfillment( Ed25519Fulfillment(public_key=VerifyingKey(vk_ilp['b58']))) fulfillment.add_subfulfillment( Ed25519Fulfillment(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 create_transfer(prev_tx, vw_sk, vw_pk, vehicle_id, metadata, asset_id): prev_tx = Transaction.from_dict(prev_tx) # Create asset VW -> [VW, TEL] # Custom crypto condition multisig 1-2 threshold_fulfillment = ThresholdSha256Fulfillment(threshold=1) vw_fulfillment = Ed25519Fulfillment(public_key=vw_pk) tel_fulfillment = Ed25519Fulfillment(public_key=vehicle_id) threshold_fulfillment.add_subfulfillment(vw_fulfillment) threshold_fulfillment.add_subfulfillment(tel_fulfillment) output = { 'amount': 1, 'condition': { 'details': threshold_fulfillment.to_dict(), 'uri': threshold_fulfillment.condition.serialize_uri() }, 'public_keys': [vw_pk, vehicle_id], } # The yet to be fulfilled input: input_ = { 'fulfillment': None, 'fulfills': { 'txid': prev_tx.id, 'output': 0, }, 'owners_before': [vw_pk, vehicle_id], } # Craft the payload: transfer_tx = { 'operation': 'TRANSFER', 'asset': { 'id': asset_id }, 'metadata': metadata, 'outputs': [output], 'inputs': [input_], 'version': '0.9', } # Generate the id, by hashing the encoded json formatted string # representation of the transaction: json_str_tx = json.dumps( transfer_tx, sort_keys=True, separators=(',', ':'), ensure_ascii=False, ) txid = sha3.sha3_256(json_str_tx.encode()).hexdigest() transfer_tx['id'] = txid # Sign the transaction: message = json.dumps( transfer_tx, sort_keys=True, separators=(',', ':'), ensure_ascii=False, ) threshold_fulfillment = ThresholdSha256Fulfillment(threshold=1) vw_fulfillment.sign(message.encode(), private_key=Ed25519SigningKey(vw_sk)) threshold_fulfillment.add_subfulfillment(vw_fulfillment) threshold_fulfillment.add_subcondition(tel_fulfillment.condition) fulfillment_uri = threshold_fulfillment.serialize_uri() transfer_tx['inputs'][0]['fulfillment'] = fulfillment_uri return Transaction.from_dict(transfer_tx)
driver=bdb) # instead of transfering to a simple public key we can craft # custom cryptoconditions using thresholds, signatures, hashlocks, ... # this example is a multisig between # # bob secret/hashlock # carly \ / # \ and # \ / # and # | # o # # the cryptocondition is added as an output condition = ThresholdSha256Fulfillment(threshold=2) condition.add_subfulfillment( Ed25519Fulfillment(public_key=Ed25519VerifyingKey(carly.public_key))) subfulfillment = ThresholdSha256Fulfillment(threshold=2) subfulfillment.add_subcondition( PreimageSha256Fulfillment(preimage=b'secret').condition) subfulfillment.add_subfulfillment( Ed25519Fulfillment(public_key=Ed25519VerifyingKey(bob.public_key))) condition.add_subfulfillment(subfulfillment) # create the transfer transaction with the custom condition tx_transfer_custom_condition = prepare_transfer( inputs=[{ 'tx': tx_create_alice_divisible_signed, 'output': 0 }],