def test_multiple_fulfillment_validation_of_transfer_tx(user_ffill, user_cond, user_priv, user2_pub, user2_priv, user3_pub, user3_priv): from copy import deepcopy from bigchaindb_common.transaction import (Transaction, TransactionLink, Fulfillment, Condition, Asset) from cryptoconditions import Ed25519Fulfillment tx = Transaction(Transaction.CREATE, Asset(), [user_ffill, deepcopy(user_ffill)], [user_cond, deepcopy(user_cond)]) tx.sign([user_priv]) fulfillments = [Fulfillment(cond.fulfillment, cond.owners_after, TransactionLink(tx.id, index)) for index, cond in enumerate(tx.conditions)] conditions = [Condition(Ed25519Fulfillment(public_key=user3_pub), [user3_pub]), Condition(Ed25519Fulfillment(public_key=user3_pub), [user3_pub])] transfer_tx = Transaction('TRANSFER', tx.asset, fulfillments, conditions) transfer_tx = transfer_tx.sign([user_priv]) assert transfer_tx.fulfillments_valid(tx.conditions) is True
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_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 persisted_transfer_dimi_car_to_ewy(dimi_keypair, ewy_pubkey, transactions_api_full_url, persisted_transfer_carol_car_to_dimi): output_txid = persisted_transfer_carol_car_to_dimi['id'] ed25519_ewy = Ed25519Fulfillment(public_key=ewy_pubkey) transaction = { 'asset': { 'id': persisted_transfer_carol_car_to_dimi['asset']['id'] }, 'metadata': None, 'operation': 'TRANSFER', 'outputs': ({ 'amount': 1, 'condition': { 'details': ed25519_ewy.to_dict(), 'uri': ed25519_ewy.condition_uri, }, 'public_keys': (ewy_pubkey, ), }, ), 'inputs': ({ 'fulfillment': None, 'fulfills': { 'output': 0, 'txid': output_txid, }, 'owners_before': (dimi_keypair.public_key, ), }, ), 'version': '0.9', } serialized_transaction_without_id = json.dumps( transaction, sort_keys=True, separators=(',', ':'), ensure_ascii=False, ).encode() transaction['id'] = sha3_256(serialized_transaction_without_id).hexdigest() serialized_transaction_with_id = json.dumps( transaction, sort_keys=True, separators=(',', ':'), ensure_ascii=False, ).encode() ed25519_dimi = Ed25519Fulfillment(public_key=dimi_keypair.public_key) ed25519_dimi.sign(serialized_transaction_with_id, Ed25519SigningKey(dimi_keypair.private_key)) transaction['inputs'][0]['fulfillment'] = ed25519_dimi.serialize_uri() response = requests.post(transactions_api_full_url, json=transaction) return response.json()
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 test_validate_fulfillments_of_transfer_tx_with_invalid_params(transfer_tx, cond_uri, utx, user2_pub, user_priv): from bigchaindb_common.transaction import Condition from cryptoconditions import Ed25519Fulfillment invalid_cond = Condition(Ed25519Fulfillment.from_uri('cf:0:'), ['invalid']) assert transfer_tx.fulfillments_valid([invalid_cond]) is False invalid_cond = utx.conditions[0] invalid_cond.owners_after = 'invalid' assert transfer_tx.fulfillments_valid([invalid_cond]) is True with raises(TypeError): assert transfer_tx.fulfillments_valid(None) is False with raises(AttributeError): transfer_tx.fulfillments_valid('not a list') with raises(ValueError): transfer_tx.fulfillments_valid([]) with raises(TypeError): transfer_tx.operation = "Operation that doesn't exist" transfer_tx.fulfillments_valid([utx.conditions[0]]) with raises(ValueError): tx = utx.sign([user_priv]) tx.conditions = [] tx.fulfillments_valid()
def test_deserialize_unsigned_dict_to_fulfillment(self, vk_ilp): fulfillment = Ed25519Fulfillment(public_key=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 create_fulfillment_ed25519sha256(self, sk_ilp, vk_ilp): sk = SigningKey(sk_ilp['b58']) vk = VerifyingKey(vk_ilp['b58']) fulfillment = Ed25519Fulfillment(public_key=vk) fulfillment.sign(MESSAGE, sk) return fulfillment
def test_generate_conditions_single_owner_with_condition(user_pub): from bigchaindb_common.transaction import Condition from cryptoconditions import Ed25519Fulfillment expected = Ed25519Fulfillment(public_key=user_pub) cond = Condition.generate([expected]) assert cond.fulfillment.to_dict() == expected.to_dict()
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 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 make_ed25519_condition(public_key, *, amount=1): ed25519 = Ed25519Fulfillment(public_key=public_key) return { 'amount': amount, 'condition': { 'details': ed25519.to_dict(), 'uri': ed25519.condition_uri, }, 'public_keys': (public_key, ), }
def test_serialize_unsigned_dict_to_fulfillment(self, vk_ilp): fulfillment = Ed25519Fulfillment(public_key=vk_ilp['b58']) assert fulfillment.to_dict() == \ {'bitmask': 32, 'public_key': 'Gtbi6WQDB6wUePiZm8aYs5XZ5pUqx9jMMLvRVHPESTjU', 'signature': None, 'type': 'fulfillment', 'type_id': 4} assert fulfillment.validate(MESSAGE) == False
def sign_transaction(transaction, *, public_key, private_key): ed25519 = Ed25519Fulfillment(public_key=public_key) message = json.dumps( transaction, sort_keys=True, separators=(',', ':'), ensure_ascii=False, ) ed25519.sign(message.encode(), Ed25519SigningKey(private_key)) return ed25519.serialize_uri()
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_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 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 sign_ed25519(transaction, private_keys): from cryptoconditions import Ed25519Fulfillment from cryptoconditions.crypto import Ed25519VerifyingKey for index, _input in enumerate(transaction.inputs): receiver = _input.owners_before[0] transaction.inputs[index].fulfillment = Ed25519Fulfillment( public_key=Ed25519VerifyingKey(receiver)) private_keys = [private_keys ] if not isinstance(private_keys, list) else private_keys return transaction.sign(private_keys).to_dict()
def test_serialize_deserialize_condition(self, vk_ilp): vk = VerifyingKey(vk_ilp['b58']) fulfillment = Ed25519Fulfillment(public_key=vk) condition = fulfillment.condition deserialized_condition = Condition.from_uri(condition.serialize_uri()) assert deserialized_condition.bitmask == condition.bitmask assert deserialized_condition.hash == condition.hash assert deserialized_condition.max_fulfillment_length == condition.max_fulfillment_length assert deserialized_condition.serialize_uri( ) == condition.serialize_uri()
def test_fulfill(self, driver, alice_keypair, unsigned_transaction): signed_transaction = driver.transactions.fulfill( unsigned_transaction, private_keys=alice_keypair.sk) unsigned_transaction['inputs'][0]['fulfillment'] = None message = json.dumps( unsigned_transaction, sort_keys=True, separators=(',', ':'), ensure_ascii=False, ).encode() ed25519 = Ed25519Fulfillment(public_key=alice_keypair.vk) ed25519.sign(message, Ed25519SigningKey(alice_keypair.sk)) fulfillment_uri = ed25519.serialize_uri() assert signed_transaction['inputs'][0]['fulfillment'] == fulfillment_uri # noqa
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 _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 prepare_transfer_ed25519_simple(transaction, receiver, metadata=None): from cryptoconditions import Ed25519Fulfillment from cryptoconditions.crypto import Ed25519VerifyingKey return prepare_transfer( inputs=[{ 'tx': transaction, 'output': 0 }], outputs=[{ 'condition': Ed25519Fulfillment(public_key=Ed25519VerifyingKey(receiver)), 'public_keys': [receiver], 'amount': 1 }], metadata=metadata)
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_serialize_deserialize_fulfillment(self, sk_ilp, vk_ilp): sk = SigningKey(sk_ilp['b58']) vk = VerifyingKey(vk_ilp['b58']) fulfillment = Ed25519Fulfillment(public_key=vk) fulfillment.sign(MESSAGE, sk) assert fulfillment.validate(MESSAGE) deserialized_fulfillment = Fulfillment.from_uri( fulfillment.serialize_uri()) assert isinstance(deserialized_fulfillment, Ed25519Fulfillment) assert deserialized_fulfillment.serialize_uri( ) == fulfillment.serialize_uri() assert deserialized_fulfillment.condition.serialize_uri( ) == fulfillment.condition.serialize_uri() assert deserialized_fulfillment.public_key.encode(encoding='bytes') == \ fulfillment.public_key.encode(encoding='bytes') assert deserialized_fulfillment.validate(MESSAGE)
def test_serialize_condition_and_validate_fulfillment( self, sk_ilp, vk_ilp, fulfillment_ed25519): sk = SigningKey(sk_ilp['b58']) vk = VerifyingKey(vk_ilp['b58']) fulfillment = Ed25519Fulfillment(public_key=vk) assert fulfillment.condition.serialize_uri( ) == fulfillment_ed25519['condition_uri'] assert binascii.hexlify(fulfillment.condition.hash ) == fulfillment_ed25519['condition_hash'] # ED25519-SHA256 condition not fulfilled assert fulfillment.validate() == False # Fulfill an ED25519-SHA256 condition fulfillment.sign(MESSAGE, sk) assert fulfillment.serialize_uri( ) == fulfillment_ed25519['fulfillment_uri'] assert fulfillment.validate(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 = 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 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)
# 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 }], outputs=[ {