bigchaindb / bigchaindb-driver

Official Python driver for BigchainDB
https://www.bigchaindb.com
Apache License 2.0
105 stars 104 forks source link

Asset Transfer Issue #330

Closed batlogs closed 7 years ago

batlogs commented 7 years ago

Description

I create Alice Alice creates an asset Alice transfers the Asset to Bob Bob wants to transfer the Asset to Rob --> Missing Key Exception (I use Bob's Private Key)

What I Did


alice = generate_keypair()
digital_asset_payload = {'data': {'msg': 'Hello BigchainDB!'}}
tx = bdb.transactions.prepare(operation='CREATE',signers=alice.public_key,asset=digital_asset_payload)
signed_tx = bdb.transactions.fulfill(tx, private_keys=alice.private_key)
sent_tx = bdb.transactions.send(signed_tx)
tx_retrieved = bdb.transactions.retrieve(tx['id'])
output_index = 0
output = tx['outputs'][output_index]
input_ = {'fulfillment': output['condition']['details'],'fulfills': {'output_index': output_index,'transaction_id': tx['id'],},'owners_before': output['public_keys'],}
transfer_asset_id = tx['id']
transfer_asset = {'id': transfer_asset_id,}
bob = generate_keypair()
tx_transfer = bdb.transactions.prepare(operation='TRANSFER',inputs=input_,asset=transfer_asset,recipients=bob.public_key,)
signed_tx_transfer = bdb.transactions.fulfill(tx_transfer,private_keys=alice.private_key,)
sent_tx_transfer = bdb.transactions.send(signed_tx_transfer)
//Transfer to Rob
transfer_asset_id = tx['id']
transfer_asset = {'id': transfer_asset_id,}
rob = generate_keypair()
tx_transfer = bdb.transactions.prepare(operation='TRANSFER',inputs=input_,asset=transfer_asset,recipients=rob.public_key,)
signed_tx_transfer = bdb.transactions.fulfill(tx_transfer,private_keys=bob.private_key,)

If there was a crash, please include the traceback here.
signed_tx_transfer = bdb.transactions.fulfill(tx_transfer,private_keys=bob.private_key,)
Traceback (most recent call last):
  File "/usr/local/lib/python3.6/site-packages/bigchaindb/common/transaction.py", line 808, in _sign_simple_signature_fulfillment
    base58.b58decode(key_pairs[public_key].encode()),
KeyError: '4V41YPsJS62S4ugvuL6sNoWCm3aD1R9PRY15Wzez9MEY'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/lib/python3.6/site-packages/bigchaindb_driver/offchain.py", line 351, in fulfill_transaction
    signed_transaction = transaction_obj.sign(private_keys)
  File "/usr/local/lib/python3.6/site-packages/bigchaindb/common/transaction.py", line 758, in sign
    self.inputs[i] = self._sign_input(input_, tx_serialized, key_pairs)
  File "/usr/local/lib/python3.6/site-packages/bigchaindb/common/transaction.py", line 779, in _sign_input
    key_pairs)
  File "/usr/local/lib/python3.6/site-packages/bigchaindb/common/transaction.py", line 813, in _sign_simple_signature_fulfillment
    .format(public_key))
bigchaindb.common.exceptions.KeypairMismatchException: Public key 4V41YPsJS62S4ugvuL6sNoWCm3aD1R9PRY15Wzez9MEY is not a pair to any of the private keys

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python3.6/site-packages/bigchaindb_driver/driver.py", line 262, in fulfill
    return fulfill_transaction(transaction, private_keys=private_keys)
  File "/usr/local/lib/python3.6/site-packages/bigchaindb_driver/offchain.py", line 353, in fulfill_transaction
    raise MissingPrivateKeyError('A private key is missing!') from exc
bigchaindb_driver.exceptions.MissingPrivateKeyError: A private key is missing!
ttmc commented 7 years ago

@batlogs I think I see the problem. When preparing the TRANSFER transaction to transfer the asset to Rob:

tx_transfer = bdb.transactions.prepare(operation='TRANSFER',inputs=input_,asset=transfer_asset,recipients=rob.public_key,)

Notice how inputs=input_ and input_ is the same input_ as was used when transferring to Bob, but it should be different because that input is supposed to fulfill a different output.

(CREATE tx) --> (TRANSFER tx 1) --> (TRANSFER tx 2)

When preparing (TRANSFER tx 1) you were fulfilling an output on (CREATE tx), but when preparing (TRANSFER tx 2), you are fulfilling an output on (TRANSFER tx 1).

batlogs commented 7 years ago

So does it mean I have to create a new asset to accomplish Transfer tx2 ? (CREATE tx1) --> (TRANSFER tx 1) --> Transfer to Bob (CREATE tx2) --> (TRANSFER tx 2) --> Transfer to Rob

ttmc commented 7 years ago

No. It's the same asset changing hands. The input for (TRANSFER tx 2) should be the output of (TRANSFER tx 1).

batlogs commented 7 years ago
#Alice Key Pair
alice = generate_keypair()
digital_asset_payload = {'data': {'msg': 'Hello BigchainDB!'}}
tx = bdb.transactions.prepare(operation='CREATE',signers=alice.public_key,asset=digital_asset_payload)
signed_tx = bdb.transactions.fulfill(tx, private_keys=alice.private_key)
sent_tx = bdb.transactions.send(signed_tx)
tx_retrieved = bdb.transactions.retrieve(tx['id'])
output_index = 0
output = tx['outputs'][output_index]
#Transfer to Bob
input_ = {'fulfillment': output['condition']['details'],'fulfills': {'output_index': output_index,'transaction_id': tx['id'],},'owners_before': output['public_keys'],}
transfer_asset_id = tx['id']
transfer_asset = {'id': transfer_asset_id,}
bob = generate_keypair()
tx_transfer = bdb.transactions.prepare(operation='TRANSFER',inputs=input_,asset=transfer_asset,recipients=bob.public_key,)
signed_tx_transfer = bdb.transactions.fulfill(tx_transfer,private_keys=alice.private_key,)
sent_tx_transfer = bdb.transactions.send(signed_tx_transfer)
#Transfer to Rob
tx_retrieved = bdb.transactions.retrieve(tx['id'])
output = tx['outputs'][output_index]
input_ = {'fulfillment': output['condition']['details'],'fulfills': {'output_index': output_index,'transaction_id': tx['id'],},'owners_before': output['public_keys'],}
transfer_asset_id = tx['id']
transfer_asset = {'id': transfer_asset_id,}
rob = generate_keypair()
tx_transfer = bdb.transactions.prepare(operation='TRANSFER',inputs=input_,asset=transfer_asset,recipients=rob.public_key,)
signed_tx_transfer = bdb.transactions.fulfill(tx_transfer,private_keys=bob.private_key,)
sent_tx_transfer = bdb.transactions.send(signed_tx_transfer)
sent_tx_transfer == signed_tx_transfer
batlogs commented 7 years ago

gives Traceback (most recent call last): File "/usr/local/lib/python3.6/site-packages/bigchaindb_driver/offchain.py", line 351, in fulfill_transaction signed_transaction = transaction_obj.sign(private_keys) File "/usr/local/lib/python3.6/site-packages/bigchaindb/common/transaction.py", line 758, in sign self.inputs[i] = self._signinput(input, tx_serialized, key_pairs) File "/usr/local/lib/python3.6/site-packages/bigchaindb/common/transaction.py", line 779, in _sign_input key_pairs) File "/usr/local/lib/python3.6/site-packages/bigchaindb/common/transaction.py", line 813, in _sign_simple_signature_fulfillment .format(public_key)) bigchaindb.common.exceptions.KeypairMismatchException: Public key FoDnJ3tQWV9N1D48pmZMKwp8BEewfgyiRxAdKx39g4UP is not a pair to any of the private keys

The above exception was the direct cause of the following exception:

Traceback (most recent call last): File "", line 1, in File "/usr/local/lib/python3.6/site-packages/bigchaindb_driver/driver.py", line 262, in fulfill return fulfill_transaction(transaction, private_keys=private_keys) File "/usr/local/lib/python3.6/site-packages/bigchaindb_driver/offchain.py", line 353, in fulfill_transaction raise MissingPrivateKeyError('A private key is missing!') from exc bigchaindb_driver.exceptions.MissingPrivateKeyError: A private key is missing!

batlogs commented 7 years ago

can you please give an example for "The input for (TRANSFER tx 2) should be the output of (TRANSFER tx 1)" and help

ttmc commented 7 years ago

I think you need to add one line after the comment #Transfer to Rob:

#Transfer to Rob
tx = sent_tx_transfer

because in the code you shared above, tx was still the original (unsigned) CREATE tx.

batlogs commented 7 years ago

I did try that, but now I get the below error

tx = sent_tx_transfer tx_retrieved = bdb.transactions.retrieve(tx['id']) output_index = 0 output = tx['outputs'][outputindex] input = {'fulfillment': output['condition']['details'],'fulfills': {'output_index': output_index,'transaction_id': tx['id'],},'owners_before': output['public_keys'],} transfer_asset_id = tx['id'] transfer_asset = {'id': transfer_asset_id,} rob = generate_keypair() txtransfer = bdb.transactions.prepare(operation='TRANSFER',inputs=input,asset=transfer_asset,recipients=rob.public_key,) signed_tx_transfer = bdb.transactions.fulfill(tx_transfer,private_keys=bob.private_key,) sent_tx_transfer = bdb.transactions.send(signed_tx_transfer) Traceback (most recent call last): File "", line 1, in File "/usr/local/lib/python3.6/site-packages/bigchaindb_driver/driver.py", line 319, in send method='POST', path=self.path, json=transaction, headers=headers) File "/usr/local/lib/python3.6/site-packages/bigchaindb_driver/transport.py", line 58, in forward_request headers=headers, File "/usr/local/lib/python3.6/site-packages/bigchaindb_driver/connection.py", line 57, in request raise exc_cls(response.status_code, text, json) bigchaindb_driver.exceptions.BadRequest: (400, '{\n "message": "Invalid transaction (AssetIdMismatch): The asset id of the input does not match the asset id of the transaction", \n "status": 400\n}\n', {'message': 'Invalid transaction (AssetIdMismatch): The asset id of the input does not match the asset id of the transaction', 'status': 400})

ttmc commented 7 years ago

Oh, right, the id of an asset is always the id of the CREATE transaction where that asset was originally created. In this case, that should be the id of (CREATE tx 1). Your code is setting transfer_asset_id = tx['id'] but now tx is the unsigned (TRANSFER tx 1), not (CREATE tx 1). One way to fix that would be to delete two lines in the #Transfer to Rob section:

transfer_asset_id = tx['id']
transfer_asset = {'id': transfer_asset_id,}

That way, those two variables will stay the same, as they should, because the asset id doesn't change.

batlogs commented 7 years ago

Brilliant! it works. Thanks for all your help

raaghavvd commented 7 years ago

alice = generate_keypair() bob= generate_keypair() matt = generate_keypair()

                        #Generates public and private key for Alice and Bob

''' Here the asset being transferred is a bicycle which belongs to Alice The variable below contains a data property which contains the information about the bicycle '''

bicycle= { 'data':{ 'bicycle':{ 'serial_number' : 'XX2334C', 'manufacturer':'bkfab', },

},

} metadata={'Country':'France'} #Any dictionary can be used as Metadata prepared_creation_tx= bdb.transactions.prepare(operation='CREATE',signers=alice.public_key,asset=bicycle,metadata=metadata) #This creates a digital asset fulfilled_creation_tx=bdb.transactions.fulfill(prepared_creation_tx,private_keys=alice.private_key) #the transaction gets authenticated using Alice's private key sent_creation_tx=bdb.transactions.send(fulfilled_creation_tx) #the authorized transaction is now sent over to the BigchainDB print(sent_creation_tx==fulfilled_creation_tx) txid= fulfilled_creation_tx['id'] #contains the transaction id print('transactions id is ') print(txid) ''' The code below keeps checking the transaction until it become valid As it takes a some time before the transaction is deemed as valid by the BigchainDB Cluster

'''

trials = 0

while trials < 100: try: if bdb.transactions.status(txid).get('status') == 'valid': break except bigchaindb_driver.exceptions.NotFoundError: trials += 1 print(bdb.transactions.status(txid))

creation_tx= fulfilled_creation_tx #Alice retrieves the transaction print("Alice retrieves") print(creation_tx)

''' Preparing a transaction To get inforamtion about the id of the asset we are transferrring ''' asset_id= creation_tx['id'] transfer_asset={ 'id' :asset_id, }

''' Transfer transaction ''' output_index=0 output= creation_tx['outputs'][output_index] transfer_input={ 'fulfillment': output['condition']['details'], 'fulfills':{ 'output_index':output_index, 'transaction_id':creation_tx['id'], }, 'owners_before': output['public_keys'], }

print("public_keys 1") print(output['public_keys']) prepared_transfer_tx = bdb.transactions.prepare( operation='TRANSFER', asset=transfer_asset, inputs=transfer_input, recipients=bob.public_key, )

Fulfillment of transfer

fulfilled_transfer_tx= bdb.transactions.fulfill( prepared_transfer_tx, private_keys= alice.private_key, )

Send it across the Node

sent_transfer_tx=bdb.transactions.send(fulfilled_transfer_tx) print(sent_transfer_tx==fulfilled_transfer_tx) print("Fulfilled transfer looks like") print(fulfilled_transfer_tx)

print("Is the bob the owner?",sent_transfer_tx['outputs'][0]['public_keys'][0]==bob.public_key) print("Was Alice the previous owner? ",fulfilled_transfer_tx['inputs'][0]['owners_before'][0]==alice.public_key)

transfer_tx=fulfilled_transfer_tx asset_id = transfer_tx['id'] transfer_asset={ 'id' :asset_id, } print ("Helllooo") print (transfer_tx['asset']['id'])

output= transfer_tx['outputs'][output_index] transfer_input={ 'fulfillment': output['condition']['details'], 'fulfills':{ 'output_index':output_index, 'transaction_id':transfer_tx['asset']['id'], }, 'owners_before': output['public_keys'], }

print("public_keys 1") print(output['public_keys']) prepared_transfer_tx = bdb.transactions.prepare( operation='TRANSFER', asset=transfer_asset, inputs=transfer_input, recipients=matt.public_key, )

Fulfillment of transfer

fulfilled_transfer_tx= bdb.transactions.fulfill( prepared_transfer_tx, private_keys= bob.private_key, )

Send it across the Node

sent_transfer_tx=bdb.transactions.send(fulfilled_transfer_tx). I get the error on this line print(sent_transfer_tx==fulfilled_transfer_tx) print("Fulfilled transfer looks like") print(fulfilled_transfer_tx)

Error message : bigchaindb_driver.exceptions.BadRequest: (400, '{\n "message": "Invalid transaction (InvalidSignature): Transaction signature is invalid.", \n "status": 400\n}\n', {'message': 'Invalid transaction (InvalidSignature): Transaction signature is invalid.', 'status': 400})

print("Is matt the owner?",sent_transfer_tx['outputs'][0]['public_keys'][0]==bob.public_key) print("Was Alice the previous owner? ",fulfilled_transfer_tx['inputs'][0]['owners_before'][0]==alice.public_key) print("Was Bob the previous owner? ",fulfilled_transfer_tx['inputs'][0]['owners_before'][1]==alice.public_key)