def test_get_owned_ids_single_tx_single_output(self, b, user_sk, user_pk, alice): from bigchaindb.common import crypto from bigchaindb.common.transaction import TransactionLink from bigchaindb.models import Transaction user2_sk, user2_pk = crypto.generate_key_pair() tx = Transaction.create([alice.public_key], [([user_pk], 1)]) tx = tx.sign([alice.private_key]) block = b.create_block([tx]) b.write_block(block) owned_inputs_user1 = b.get_owned_ids(user_pk) owned_inputs_user2 = b.get_owned_ids(user2_pk) assert owned_inputs_user1 == [TransactionLink(tx.id, 0)] assert owned_inputs_user2 == [] tx = Transaction.transfer(tx.to_inputs(), [([user2_pk], 1)], asset_id=tx.id) tx = tx.sign([user_sk]) block = b.create_block([tx]) b.write_block(block) owned_inputs_user1 = b.get_owned_ids(user_pk) owned_inputs_user2 = b.get_owned_ids(user2_pk) assert owned_inputs_user1 == [] assert owned_inputs_user2 == [TransactionLink(tx.id, 0)]
def test_transaction_link_deserialization_with_empty_payload(): from bigchaindb.common.transaction import TransactionLink expected = TransactionLink() tx_link = TransactionLink.from_dict(None) assert tx_link == expected
def test_get_outputs_by_public_key(b, user_pk, user2_pk, blockdata): blocks, _ = blockdata assert b.fastquery.get_outputs_by_public_key(user_pk) == [ TransactionLink(blocks[1]['block']['transactions'][0]['id'], 0) ] assert b.fastquery.get_outputs_by_public_key(user2_pk) == [ TransactionLink(blocks[0]['block']['transactions'][0]['id'], 0) ]
def test_cast_transaction_link_to_boolean(): from bigchaindb.common.transaction import TransactionLink assert bool(TransactionLink()) is False assert bool(TransactionLink('a', None)) is False assert bool(TransactionLink(None, 'b')) is False assert bool(TransactionLink('a', 'b')) is True assert bool(TransactionLink(False, False)) is True
def test_get_outputs_by_public_key(b, user_pk, user2_pk, txns): assert b.fastquery.get_outputs_by_public_key(user_pk) == [ TransactionLink(txns[1].id, 0), TransactionLink(txns[2].id, 0) ] assert b.fastquery.get_outputs_by_public_key(user2_pk) == [ TransactionLink(txns[0].id, 0), TransactionLink(txns[2].id, 1), ]
def test_transaction_link_serialization(): from bigchaindb.common.transaction import TransactionLink tx_id = 'a transaction id' expected = { 'transaction_id': tx_id, 'output_index': 0, } tx_link = TransactionLink(tx_id, 0) assert tx_link.to_dict() == expected
def test_get_outputs_filtered_only_spent(): from bigchaindb.common.transaction import TransactionLink from bigchaindb.lib import BigchainDB go = 'bigchaindb.fastquery.FastQuery.get_outputs_by_public_key' with patch(go) as get_outputs: get_outputs.return_value = [TransactionLink('a', 1), TransactionLink('b', 2)] fs = 'bigchaindb.fastquery.FastQuery.filter_unspent_outputs' with patch(fs) as filter_spent: filter_spent.return_value = [TransactionLink('b', 2)] out = BigchainDB().get_outputs_filtered('abc', spent=True) get_outputs.assert_called_once_with('abc') assert out == [TransactionLink('b', 2)]
def test_get_outputs_filtered(filter_spent, filter_unspent): from bigchaindb.common.transaction import TransactionLink from bigchaindb.lib import BigchainDB go = 'bigchaindb.fastquery.FastQuery.get_outputs_by_public_key' with patch(go) as get_outputs: get_outputs.return_value = [TransactionLink('a', 1), TransactionLink('b', 2)] out = BigchainDB().get_outputs_filtered('abc') get_outputs.assert_called_once_with('abc') filter_spent.assert_not_called() filter_unspent.assert_not_called() assert out == get_outputs.return_value
def test_transaction_link_eq(): from bigchaindb.common.transaction import TransactionLink assert TransactionLink(1, 2) == TransactionLink(1, 2) assert TransactionLink(2, 2) != TransactionLink(1, 2) assert TransactionLink(1, 1) != TransactionLink(1, 2) assert TransactionLink(2, 1) != TransactionLink(1, 2)
def get_owned_ids(self, owner): """Retrieve a list of `txid`s that can be used as inputs. Args: owner (str): base58 encoded public key. Returns: :obj:`list` of TransactionLink: list of `txid`s and `cid`s pointing to another transaction's condition """ # get all transactions in which owner is in the `owners_after` list response = self.connection.run( r.table('bigchain', read_mode=self.read_mode).concat_map( lambda doc: doc['block']['transactions']).filter( lambda tx: tx['transaction']['conditions'].contains( lambda c: c['owners_after'].contains(owner)))) owned = [] for tx in response: # disregard transactions from invalid blocks validity = self.get_blocks_status_containing_tx(tx['id']) if Bigchain.BLOCK_VALID not in validity.values(): if Bigchain.BLOCK_UNDECIDED not in validity.values(): continue # NOTE: It's OK to not serialize the transaction here, as we do not # use it after the execution of this function. # a transaction can contain multiple outputs (conditions) so we need to iterate over all of them # to get a list of outputs available to spend for index, cond in enumerate(tx['transaction']['conditions']): # for simple signature conditions there are no subfulfillments # check if the owner is in the condition `owners_after` if len(cond['owners_after']) == 1: if cond['condition']['details']['public_key'] == owner: tx_link = TransactionLink(tx['id'], index) else: # for transactions with multiple `owners_after` there will be several subfulfillments nested # in the condition. We need to iterate the subfulfillments to make sure there is a # subfulfillment for `owner` if util.condition_details_has_owner( cond['condition']['details'], owner): tx_link = TransactionLink(tx['id'], index) # check if input was already spent if not self.get_spent(tx_link.txid, tx_link.cid): owned.append(tx_link) return owned
def transfer_utx(user_output, user2_output, utx): from bigchaindb.common.transaction import (Input, TransactionLink, Transaction) user_output = user_output.to_dict() input = Input(utx.outputs[0].fulfillment, user_output['public_keys'], TransactionLink(utx.id, 0)) return Transaction('TRANSFER', {'id': utx.id}, [input], [user2_output])
def test_get_owned_ids_multiple_owners(self, b, user_sk, user_pk, alice): from bigchaindb.common import crypto from bigchaindb.common.transaction import TransactionLink from bigchaindb.models import Transaction user2_sk, user2_pk = crypto.generate_key_pair() user3_sk, user3_pk = crypto.generate_key_pair() tx = Transaction.create([alice.public_key], [([user_pk, user2_pk], 1)]) tx = tx.sign([alice.private_key]) b.store_bulk_transactions([tx]) owned_inputs_user1 = b.fastquery.get_outputs_by_public_key(user_pk) owned_inputs_user2 = b.fastquery.get_outputs_by_public_key(user_pk) expected_owned_inputs_user1 = [TransactionLink(tx.id, 0)] assert owned_inputs_user1 == owned_inputs_user2 assert owned_inputs_user1 == expected_owned_inputs_user1 tx = Transaction.transfer(tx.to_inputs(), [([user3_pk], 1)], asset_id=tx.id) tx = tx.sign([user_sk, user2_sk]) b.store_bulk_transactions([tx]) owned_inputs_user1 = b.fastquery.get_outputs_by_public_key(user_pk) owned_inputs_user2 = b.fastquery.get_outputs_by_public_key(user2_pk) spent_user1 = b.get_spent(tx.id, 0) assert owned_inputs_user1 == owned_inputs_user2 assert not spent_user1
def test_non_create_input_not_found(self, b, user_pk, signed_transfer_tx): from bigchaindb.common.exceptions import InputDoesNotExist from bigchaindb.common.transaction import TransactionLink signed_transfer_tx.inputs[0].fulfills = TransactionLink('c', 0) with pytest.raises(InputDoesNotExist): b.validate_transaction(signed_transfer_tx)
def test_transaction_link_to_uri(): from bigchaindb.common.transaction import TransactionLink expected = 'path/transactions/abc/outputs/0' tx_link = TransactionLink('abc', 0).to_uri('path') assert expected == tx_link
def test_multiple_input_validation_of_transfer_tx(user_input, user_output, user_priv, user2_pub, user2_priv, user3_pub, user3_priv, asset_definition): from bigchaindb.common.transaction import (Transaction, TransactionLink, Input, Output) from cryptoconditions import Ed25519Sha256 from .utils import validate_transaction_model tx = Transaction(Transaction.CREATE, asset_definition, [user_input], [user_output, deepcopy(user_output)]) tx.sign([user_priv]) inputs = [ Input(cond.fulfillment, cond.public_keys, TransactionLink(tx.id, index)) for index, cond in enumerate(tx.outputs) ] outputs = [ Output(Ed25519Sha256(public_key=b58decode(user3_pub)), [user3_pub]), Output(Ed25519Sha256(public_key=b58decode(user3_pub)), [user3_pub]) ] transfer_tx = Transaction('TRANSFER', {'id': tx.id}, inputs, outputs) transfer_tx = transfer_tx.sign([user_priv]) assert transfer_tx.inputs_valid(tx.outputs) is True validate_transaction_model(tx)
def test_transaction_link_empty_to_uri(): from bigchaindb.common.transaction import TransactionLink expected = None tx_link = TransactionLink().to_uri() assert expected == tx_link
def get_outputs_by_public_key(self, public_key): """Get outputs for a public key""" txs = list(query.get_owned_ids(self.connection, public_key)) return [TransactionLink(tx['id'], index) for tx in txs for index, output in enumerate(tx['outputs']) if condition_details_has_owner(output['condition']['details'], public_key)]
def prepare_transfer(inputs, outputs, metadata=None): """Create an instance of a :class:`~.Output`. Args: inputs (list of (dict): { 'tx': <(bigchaindb.common.transactionTransaction): input transaction, can differ but must have same asset id>, 'output': <(int): output index of tx> } ) outputs (list of (dict): { 'condition': <(cryptoconditions.Condition): output condition>, 'public_keys': <(optional list of base58): for indexing defaults to `None`>, 'amount': <(int): defaults to `1`> } ) metadata (dict) Raises: TypeError: if `public_keys` is not instance of `list`. """ from bigchaindb.common.transaction import (Input, Output, TransactionLink) from bigchaindb.models import Transaction from cryptoconditions import (Fulfillment, Condition) asset = inputs[0]['tx']['asset'] asset = {'id': asset['id'] if 'id' in asset else inputs[0]['tx']['id']} _inputs, _outputs = [], [] for _input in inputs: _output = _input['tx']['outputs'][_input['output']] _inputs.append( Input(fulfillment=Condition.from_uri(_output['condition']['uri']), owners_before=_output['public_keys'], fulfills=TransactionLink(txid=_input['tx']['id'], output=_input['output']))) for output in outputs: _outputs.append( Output(fulfillment=output['condition'], public_keys=output['public_keys'] if "public_keys" in output else [], amount=output['amount'] if "amount" in output else 1)) return Transaction( operation='TRANSFER', asset=asset, inputs=_inputs, outputs=_outputs, metadata=metadata, )
def test_get_owned_ids_single_tx_single_output_invalid_block(self, b, user_sk, user_pk, genesis_block, alice): from bigchaindb.common import crypto from bigchaindb.common.transaction import TransactionLink from bigchaindb.models import Transaction user2_sk, user2_pk = crypto.generate_key_pair() tx = Transaction.create([alice.public_key], [([user_pk], 1)]) tx = tx.sign([alice.private_key]) block = b.create_block([tx]) b.write_block(block) # vote the block VALID vote = b.vote(block.id, genesis_block.id, True) b.write_vote(vote) owned_inputs_user1 = b.get_owned_ids(user_pk) owned_inputs_user2 = b.get_owned_ids(user2_pk) assert owned_inputs_user1 == [TransactionLink(tx.id, 0)] assert owned_inputs_user2 == [] # NOTE: The transaction itself is valid, still will mark the block # as invalid to mock the behavior. tx_invalid = Transaction.transfer(tx.to_inputs(), [([user2_pk], 1)], asset_id=tx.id) tx_invalid = tx_invalid.sign([user_sk]) block = b.create_block([tx_invalid]) b.write_block(block) # vote the block invalid vote = b.vote(block.id, b.get_last_voted_block().id, False) b.write_vote(vote) owned_inputs_user1 = b.get_owned_ids(user_pk) owned_inputs_user2 = b.get_owned_ids(user2_pk) # should be the same as before (note tx, not tx_invalid) assert owned_inputs_user1 == [TransactionLink(tx.id, 0)] assert owned_inputs_user2 == []
def get_outputs_by_public_key(self, public_key): """ Get outputs for a public key """ res = list(query.get_owned_ids(self.connection, public_key)) txs = [tx for _, tx in self.filter_valid_items(res)] return [ TransactionLink(tx['id'], index) for tx in txs for index, output in enumerate(tx['outputs']) if output_has_owner(output, public_key) ]
def test_get_owned_ids_single_tx_multiple_outputs(self, b, user_sk, user_pk, alice): from bigchaindb.common import crypto from bigchaindb.common.transaction import TransactionLink from bigchaindb.models import Transaction user2_sk, user2_pk = crypto.generate_key_pair() # create divisible asset tx_create = Transaction.create([alice.public_key], [([user_pk], 1), ([user_pk], 1)]) tx_create_signed = tx_create.sign([alice.private_key]) block = b.create_block([tx_create_signed]) b.write_block(block) # get input owned_inputs_user1 = b.get_owned_ids(user_pk) owned_inputs_user2 = b.get_owned_ids(user2_pk) expected_owned_inputs_user1 = [ TransactionLink(tx_create.id, 0), TransactionLink(tx_create.id, 1) ] assert owned_inputs_user1 == expected_owned_inputs_user1 assert owned_inputs_user2 == [] # transfer divisible asset divided in two outputs tx_transfer = Transaction.transfer(tx_create.to_inputs(), [([user2_pk], 1), ([user2_pk], 1)], asset_id=tx_create.id) tx_transfer_signed = tx_transfer.sign([user_sk]) block = b.create_block([tx_transfer_signed]) b.write_block(block) owned_inputs_user1 = b.get_owned_ids(user_pk) owned_inputs_user2 = b.get_owned_ids(user2_pk) assert owned_inputs_user1 == [] assert owned_inputs_user2 == [ TransactionLink(tx_transfer.id, 0), TransactionLink(tx_transfer.id, 1) ]
def get_outputs(self, owner): """Retrieve a list of links to transaction outputs for a given public key. Args: owner (str): base58 encoded public key. Returns: :obj:`list` of TransactionLink: list of ``txid`` s and ``output`` s pointing to another transaction's condition """ # get all transactions in which owner is in the `owners_after` list response = backend.query.get_owned_ids(self.connection, owner) links = [] for tx in response: # disregard transactions from invalid blocks validity = self.get_blocks_status_containing_tx(tx['id']) if Bigchain.BLOCK_VALID not in validity.values(): if Bigchain.BLOCK_UNDECIDED not in validity.values(): continue # NOTE: It's OK to not serialize the transaction here, as we do not # use it after the execution of this function. # a transaction can contain multiple outputs so we need to iterate over all of them # to get a list of outputs available to spend for index, output in enumerate(tx['outputs']): # for simple signature conditions there are no subfulfillments # check if the owner is in the condition `owners_after` if len(output['public_keys']) == 1: if output['condition']['details']['public_key'] == owner: tx_link = TransactionLink(tx['id'], index) else: # for transactions with multiple `public_keys` there will be several subfulfillments nested # in the condition. We need to iterate the subfulfillments to make sure there is a # subfulfillment for `owner` if utils.condition_details_has_owner( output['condition']['details'], owner): tx_link = TransactionLink(tx['id'], index) links.append(tx_link) return links
def filter_unspent_outputs(self, outputs): """Remove outputs that have not been spent Args: outputs: list of TransactionLink """ links = [o.to_dict() for o in outputs] txs = list(query.get_spending_transactions(self.connection, links)) spends = {TransactionLink.from_dict(input_['fulfills']) for tx in txs for input_ in tx['inputs']} return [ff for ff in outputs if ff in spends]
def filter_unspent_outputs(self, outputs): """Remove outputs that have not been spent Args: outputs: list of TransactionLink """ links = [o.to_dict() for o in outputs] txs = list(query.get_spending_transactions(self.connection, links)) spends = {TransactionLink.from_dict(input_['fulfills']) for tx in txs for input_ in tx['inputs']} return [ff for ff in outputs if ff in spends]
def filter_spent_outputs(self, outputs): """Remove outputs that have been spent Args: outputs: list of TransactionLink """ links = [o.to_dict() for o in outputs] res = query.get_spending_transactions(self.connection, links) txs = [tx for _, tx in self.filter_valid_items(res)] spends = {TransactionLink.from_dict(input_['fulfills']) for tx in txs for input_ in tx['inputs']} return [ff for ff in outputs if ff not in spends]
def test_non_create_input_not_found(self, b, user_pk): from cryptoconditions import Ed25519Sha256 from bigchaindb.common.exceptions import InputDoesNotExist from bigchaindb.common.transaction import Input, TransactionLink from bigchaindb.models import Transaction # Create an input for a non existing transaction input = Input(Ed25519Sha256(public_key=b58decode(user_pk)), [user_pk], TransactionLink('somethingsomething', 0)) tx = Transaction.transfer([input], [([user_pk], 1)], asset_id='mock_asset_link') with pytest.raises(InputDoesNotExist): tx.validate(b)
def filter_spent_outputs(self, outputs): """Remove outputs that have been spent Args: outputs: list of TransactionLink """ links = [o.to_dict() for o in outputs] res = query.get_spending_transactions(self.connection, links) txs = [tx for _, tx in self.filter_valid_items(res)] spends = { TransactionLink.from_dict(input_['fulfills']) for tx in txs for input_ in tx['inputs'] } return [ff for ff in outputs if ff not in spends]
def get_outputs(self, owner): """Retrieve a list of links to transaction outputs for a given public key. Args: owner (str): base58 encoded public key. Returns: :obj:`list` of TransactionLink: list of ``txid`` s and ``output`` s pointing to another transaction's condition """ # get all transactions in which owner is in the `owners_after` list response = backend.query.get_owned_ids(self.connection, owner) return [ TransactionLink(tx['id'], index) for tx in response if not self.is_tx_strictly_in_invalid_block(tx['id']) for index, output in enumerate(tx['outputs']) if utils.output_has_owner(output, owner) ]
def prepare_transfer_transaction(*, inputs, recipients, asset, metadata=None): """ Prepares a ``"TRANSFER"`` transaction payload, ready to be fulfilled. Args: inputs (:obj:`dict` | :obj:`list` | :obj:`tuple`): One or more inputs holding the condition(s) that this transaction intends to fulfill. Each input is expected to be a :obj:`dict`. recipients (:obj:`str` | :obj:`list` | :obj:`tuple`): One or more public keys representing the new recipients(s) of the asset being transferred. asset (:obj:`dict`): A single-key dictionary holding the ``id`` of the asset being transferred with this transaction. metadata (:obj:`dict`): Metadata associated with the transaction. Defaults to ``None``. Returns: dict: The prepared ``"TRANSFER"`` transaction. .. important:: * ``asset`` MUST be in the form of:: { 'id': '<Asset ID (i.e. TX ID of its CREATE transaction)>' } Example: .. todo:: Replace this section with docs. In case it may not be clear what an input should look like, say Alice (public key: ``'3Cxh1eKZk3Wp9KGBWFS7iVde465UvqUKnEqTg2MW4wNf'``) wishes to transfer an asset over to Bob (public key: ``'EcRawy3Y22eAUSS94vLF8BVJi62wbqbD9iSUSUNU9wAA'``). Let the asset creation transaction payload be denoted by ``tx``:: # noqa E501 >>> tx {'id': '57cff2b9490468bdb6d4767a1b07905fdbe18d638d9c7783f639b4b2bc165c39', 'transaction': {'asset': {'data': {'msg': 'Hello BigchainDB!'}, 'id': '57cff2b9490468bdb6d4767a1b07905fdbe18d638d9c7783f639b4b2bc165c39'}, 'conditions': [{'amount': 1, 'cid': 0, 'condition': {'details': {'bitmask': 32, 'public_key': '3Cxh1eKZk3Wp9KGBWFS7iVde465UvqUKnEqTg2MW4wNf', 'signature': None, 'type': 'fulfillment', 'type_id': 4}, 'uri': 'cc:4:20:IMe7QSL5xRAYIlXon76ZonWktR0NI02M8rAG1bN-ugg:96'}, 'owners_after': ['3Cxh1eKZk3Wp9KGBWFS7iVde465UvqUKnEqTg2MW4wNf']}], 'fulfillments': [{'fid': 0, 'fulfillment': 'cf:4:IMe7QSL5xRAYIlXon76ZonWktR0NI02M8rAG1bN-ughA8-9lUJYc_LGAB_NtyTPCCV58LfMcNZ9-0PUB6m1y_6pgTbCOQFBEeDtm_nC293CbpZjziwq7j3skrzS-OiAI', 'input': None, 'owners_before': ['3Cxh1eKZk3Wp9KGBWFS7iVde465UvqUKnEqTg2MW4wNf']}], 'metadata': None, 'operation': 'CREATE', 'timestamp': '1479393278'}, 'version': 1} Then, the input may be constructed in this way:: cid = 0 condition = tx['transaction']['conditions'][cid] input_ = { 'fulfillment': condition['condition']['details'], 'input': { 'cid': cid, 'txid': tx['id'], }, 'owners_before': condition['owners_after'], } Displaying the input on the prompt would look like:: >>> input_ {'fulfillment': {'bitmask': 32, 'public_key': '3Cxh1eKZk3Wp9KGBWFS7iVde465UvqUKnEqTg2MW4wNf', 'signature': None, 'type': 'fulfillment', 'type_id': 4}, 'input': {'cid': 0, 'txid': '57cff2b9490468bdb6d4767a1b07905fdbe18d638d9c7783f639b4b2bc165c39'}, 'owners_before': ['3Cxh1eKZk3Wp9KGBWFS7iVde465UvqUKnEqTg2MW4wNf']} To prepare the transfer: >>> prepare_transfer_transaction( ... inputs=input_, ... recipients='EcRawy3Y22eAUSS94vLF8BVJi62wbqbD9iSUSUNU9wAA', ... asset=tx['transaction']['asset'], ... ) """ if not isinstance(inputs, (list, tuple)): inputs = (inputs, ) if not isinstance(recipients, (list, tuple)): recipients = [([recipients], 1)] # NOTE: Needed for the time being. See # https://github.com/bigchaindb/bigchaindb/issues/797 if isinstance(recipients, tuple): recipients = [(list(recipients), 1)] fulfillments = [ Input(Fulfillment.from_dict(input_['fulfillment']), input_['owners_before'], fulfills=TransactionLink(**input_['fulfills'])) for input_ in inputs ] transaction = Transaction.transfer( fulfillments, recipients, asset_id=asset['id'], metadata=metadata, ) return transaction.to_dict()
def main(): """ Main function """ ctx = {} def pretty_json(data): return json.dumps(data, indent=2, sort_keys=True) client = server.create_app().test_client() host = 'example.com:9984' # HTTP Index res = client.get('/', environ_overrides={'HTTP_HOST': host}) res_data = json.loads(res.data.decode()) ctx['index'] = pretty_json(res_data) # API index res = client.get('/api/v1/', environ_overrides={'HTTP_HOST': host}) ctx['api_index'] = pretty_json(json.loads(res.data.decode())) # tx create privkey = 'CfdqtD7sS7FgkMoGPXw55MVGGFwQLAoHYTcBhZDtF99Z' pubkey = '4K9sWUMFwTgaDGPfdynrbxWqWS6sWmKbZoTjxLtVUibD' asset = {'msg': 'Hello BigchainDB!'} tx = Transaction.create([pubkey], [([pubkey], 1)], asset=asset, metadata={'sequence': 0}) tx = tx.sign([privkey]) ctx['tx'] = pretty_json(tx.to_dict()) ctx['public_keys'] = tx.outputs[0].public_keys[0] ctx['txid'] = tx.id # tx transfer privkey_transfer = '3AeWpPdhEZzWLYfkfYHBfMFC2r1f8HEaGS9NtbbKssya' pubkey_transfer = '3yfQPHeWAa1MxTX9Zf9176QqcpcnWcanVZZbaHb8B3h9' cid = 0 input_ = Input(fulfillment=tx.outputs[cid].fulfillment, fulfills=TransactionLink(txid=tx.id, output=cid), owners_before=tx.outputs[cid].public_keys) tx_transfer = Transaction.transfer([input_], [([pubkey_transfer], 1)], asset_id=tx.id, metadata={'sequence': 1}) tx_transfer = tx_transfer.sign([privkey]) ctx['tx_transfer'] = pretty_json(tx_transfer.to_dict()) ctx['public_keys_transfer'] = tx_transfer.outputs[0].public_keys[0] ctx['tx_transfer_id'] = tx_transfer.id # privkey_transfer_last = 'sG3jWDtdTXUidBJK53ucSTrosktG616U3tQHBk81eQe' pubkey_transfer_last = '3Af3fhhjU6d9WecEM9Uw5hfom9kNEwE7YuDWdqAUssqm' cid = 0 input_ = Input(fulfillment=tx_transfer.outputs[cid].fulfillment, fulfills=TransactionLink(txid=tx_transfer.id, output=cid), owners_before=tx_transfer.outputs[cid].public_keys) tx_transfer_last = Transaction.transfer([input_], [([pubkey_transfer_last], 1)], asset_id=tx.id, metadata={'sequence': 2}) tx_transfer_last = tx_transfer_last.sign([privkey_transfer]) ctx['tx_transfer_last'] = pretty_json(tx_transfer_last.to_dict()) ctx['tx_transfer_last_id'] = tx_transfer_last.id ctx['public_keys_transfer_last'] = tx_transfer_last.outputs[0].public_keys[ 0] # block node_private = "5G2kE1zJAgTajkVSbPAQWo4c2izvtwqaNHYsaNpbbvxX" node_public = "DngBurxfeNVKZWCEcDnLj1eMPAS7focUZTE5FndFGuHT" signature = "53wxrEQDYk1dXzmvNSytbCfmNVnPqPkDQaTnAe8Jf43s6ssejPxezkCvUnGTnduNUmaLjhaan1iRLi3peu6s5DzA" app_hash = 'f6e0c49c6d94d6924351f25bb334cf2a99af4206339bf784e741d1a5ab599056' block = lib.Block(height=1, transactions=[tx.to_dict()], app_hash=app_hash) block_dict = block._asdict() block_dict.pop('app_hash') ctx['block'] = pretty_json(block_dict) ctx['blockid'] = block.height # block status block_list = [block.height] ctx['block_list'] = pretty_json(block_list) base_path = os.path.join(os.path.dirname(__file__), 'source/http-samples') if not os.path.exists(base_path): os.makedirs(base_path) for name, tpl in TPLS.items(): path = os.path.join(base_path, name + '.http') code = tpl % ctx with open(path, 'w') as handle: handle.write(code)
def prepare_transfer_transaction(*, inputs, recipients, asset, metadata=None): """Prepares a ``"TRANSFER"`` transaction payload, ready to be fulfilled. Args: inputs (:obj:`dict` | :obj:`list` | :obj:`tuple`): One or more inputs holding the condition(s) that this transaction intends to fulfill. Each input is expected to be a :obj:`dict`. recipients (:obj:`str` | :obj:`list` | :obj:`tuple`): One or more public keys representing the new recipients(s) of the asset being transferred. asset (:obj:`dict`): A single-key dictionary holding the ``id`` of the asset being transferred with this transaction. metadata (:obj:`dict`): Metadata associated with the transaction. Defaults to ``None``. Returns: dict: The prepared ``"TRANSFER"`` transaction. .. important:: * ``asset`` MUST be in the form of:: { 'id': '<Asset ID (i.e. TX ID of its CREATE transaction)>' } Example: .. todo:: Replace this section with docs. In case it may not be clear what an input should look like, say Alice (public key: ``'3Cxh1eKZk3Wp9KGBWFS7iVde465UvqUKnEqTg2MW4wNf'``) wishes to transfer an asset over to Bob (public key: ``'EcRawy3Y22eAUSS94vLF8BVJi62wbqbD9iSUSUNU9wAA'``). Let the asset creation transaction payload be denoted by ``tx``:: # noqa E501 >>> tx {'asset': {'data': {'msg': 'Hello BigchainDB!'}}, 'id': '9650055df2539223586d33d273cb8fd05bd6d485b1fef1caf7c8901a49464c87', 'inputs': [{'fulfillment': {'public_key': '3Cxh1eKZk3Wp9KGBWFS7iVde465UvqUKnEqTg2MW4wNf', 'type': 'ed25519-sha-256'}, 'fulfills': None, 'owners_before': ['3Cxh1eKZk3Wp9KGBWFS7iVde465UvqUKnEqTg2MW4wNf']}], 'metadata': None, 'operation': 'CREATE', 'outputs': [{'amount': '1', 'condition': {'details': {'public_key': '3Cxh1eKZk3Wp9KGBWFS7iVde465UvqUKnEqTg2MW4wNf', 'type': 'ed25519-sha-256'}, 'uri': 'ni:///sha-256;7ApQLsLLQgj5WOUipJg1txojmge68pctwFxvc3iOl54?fpt=ed25519-sha-256&cost=131072'}, 'public_keys': ['3Cxh1eKZk3Wp9KGBWFS7iVde465UvqUKnEqTg2MW4wNf']}], 'version': '2.0'} Then, the input may be constructed in this way:: output_index output = tx['transaction']['outputs'][output_index] input_ = { 'fulfillment': output['condition']['details'], 'input': { 'output_index': output_index, 'transaction_id': tx['id'], }, 'owners_before': output['owners_after'], } Displaying the input on the prompt would look like:: >>> input_ {'fulfillment': { 'public_key': '3Cxh1eKZk3Wp9KGBWFS7iVde465UvqUKnEqTg2MW4wNf', 'type': 'ed25519-sha-256'}, 'input': {'output_index': 0, 'transaction_id': '9650055df2539223586d33d273cb8fd05bd6d485b1fef1caf7c8901a49464c87'}, 'owners_before': ['3Cxh1eKZk3Wp9KGBWFS7iVde465UvqUKnEqTg2MW4wNf']} To prepare the transfer: >>> prepare_transfer_transaction( ... inputs=input_, ... recipients='EcRawy3Y22eAUSS94vLF8BVJi62wbqbD9iSUSUNU9wAA', ... asset=tx['transaction']['asset'], ... ) """ if not isinstance(inputs, (list, tuple)): inputs = (inputs, ) if not isinstance(recipients, (list, tuple)): recipients = [([recipients], 1)] # NOTE: Needed for the time being. See # https://github.com/bigchaindb/bigchaindb/issues/797 if isinstance(recipients, tuple): recipients = [(list(recipients), 1)] fulfillments = [ Input(_fulfillment_from_details(input_['fulfillment']), input_['owners_before'], fulfills=TransactionLink( txid=input_['fulfills']['transaction_id'], output=input_['fulfills']['output_index'])) for input_ in inputs ] transaction = Transaction.transfer( fulfillments, recipients, asset_id=asset['id'], metadata=metadata, ) return transaction.to_dict()
def main(): """ Main function """ ctx = {} def pretty_json(data): return json.dumps(data, indent=2, sort_keys=True) client = server.create_app().test_client() host = 'example.com:9984' # HTTP Index res = client.get('/', environ_overrides={'HTTP_HOST': host}) res_data = json.loads(res.data.decode()) res_data['keyring'] = [ "6qHyZew94NMmUTYyHnkZsB8cxJYuRNEiEpXHe1ih9QX3", "AdDuyrTyjrDt935YnFu4VBCVDhHtY2Y6rcy7x2TFeiRi" ] res_data['public_key'] = 'NC8c8rYcAhyKVpx1PCV65CBmyq4YUbLysy3Rqrg8L8mz' ctx['index'] = pretty_json(res_data) # API index res = client.get('/api/v1/', environ_overrides={'HTTP_HOST': host}) ctx['api_index'] = pretty_json(json.loads(res.data.decode())) # tx create privkey = 'CfdqtD7sS7FgkMoGPXw55MVGGFwQLAoHYTcBhZDtF99Z' pubkey = '4K9sWUMFwTgaDGPfdynrbxWqWS6sWmKbZoTjxLtVUibD' asset = {'msg': 'Hello BigchainDB!'} tx = Transaction.create([pubkey], [([pubkey], 1)], asset=asset, metadata={'sequence': 0}) tx = tx.sign([privkey]) ctx['tx'] = pretty_json(tx.to_dict()) ctx['public_keys'] = tx.outputs[0].public_keys[0] ctx['txid'] = tx.id # tx transfer privkey_transfer = '3AeWpPdhEZzWLYfkfYHBfMFC2r1f8HEaGS9NtbbKssya' pubkey_transfer = '3yfQPHeWAa1MxTX9Zf9176QqcpcnWcanVZZbaHb8B3h9' cid = 0 input_ = Input(fulfillment=tx.outputs[cid].fulfillment, fulfills=TransactionLink(txid=tx.id, output=cid), owners_before=tx.outputs[cid].public_keys) tx_transfer = Transaction.transfer([input_], [([pubkey_transfer], 1)], asset_id=tx.id, metadata={'sequence': 1}) tx_transfer = tx_transfer.sign([privkey]) ctx['tx_transfer'] = pretty_json(tx_transfer.to_dict()) ctx['public_keys_transfer'] = tx_transfer.outputs[0].public_keys[0] ctx['tx_transfer_id'] = tx_transfer.id # privkey_transfer_last = 'sG3jWDtdTXUidBJK53ucSTrosktG616U3tQHBk81eQe' pubkey_transfer_last = '3Af3fhhjU6d9WecEM9Uw5hfom9kNEwE7YuDWdqAUssqm' cid = 0 input_ = Input(fulfillment=tx_transfer.outputs[cid].fulfillment, fulfills=TransactionLink(txid=tx_transfer.id, output=cid), owners_before=tx_transfer.outputs[cid].public_keys) tx_transfer_last = Transaction.transfer([input_], [([pubkey_transfer_last], 1)], asset_id=tx.id, metadata={'sequence': 2}) tx_transfer_last = tx_transfer_last.sign([privkey_transfer]) ctx['tx_transfer_last'] = pretty_json(tx_transfer_last.to_dict()) ctx['tx_transfer_last_id'] = tx_transfer_last.id ctx['public_keys_transfer_last'] = tx_transfer_last.outputs[0].public_keys[ 0] # block node_private = "5G2kE1zJAgTajkVSbPAQWo4c2izvtwqaNHYsaNpbbvxX" node_public = "DngBurxfeNVKZWCEcDnLj1eMPAS7focUZTE5FndFGuHT" signature = "53wxrEQDYk1dXzmvNSytbCfmNVnPqPkDQaTnAe8Jf43s6ssejPxezkCvUnGTnduNUmaLjhaan1iRLi3peu6s5DzA" block = Block(transactions=[tx], node_pubkey=node_public, voters=[node_public], signature=signature) ctx['block'] = pretty_json(block.to_dict()) ctx['blockid'] = block.id block_transfer = Block(transactions=[tx_transfer], node_pubkey=node_public, voters=[node_public], signature=signature) ctx['block_transfer'] = pretty_json(block.to_dict()) # vote DUMMY_SHA3 = '0123456789abcdef' * 4 b = Bigchain(public_key=node_public, private_key=node_private) vote = b.vote(block.id, DUMMY_SHA3, True) ctx['vote'] = pretty_json(vote) # block status block_list = [block_transfer.id, block.id] ctx['block_list'] = pretty_json(block_list) base_path = os.path.join(os.path.dirname(__file__), 'source/http-samples') if not os.path.exists(base_path): os.makedirs(base_path) for name, tpl in TPLS.items(): path = os.path.join(base_path, name + '.http') code = tpl % ctx with open(path, 'w') as handle: handle.write(code)