bigchaindb / bigchaindb-driver

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

Provide a transaction/fulfillment/condition validation utility #110

Open sbellem opened 7 years ago

sbellem commented 7 years ago

To support cases similar to:

>>> # Create another transfer transaction with the same input
>>> tx_transfer2 = b.create_transaction(testuser1_pub, testuser2_pub, tx_retrieved_id, 'TRANSFER')
>>>
>>> # Sign the transaction
>>> tx_transfer_signed2 = b.sign_transaction(tx_transfer2, testuser1_priv)
>>>
>>> # Check if the transaction is valid
>>> b.validate_transaction(tx_transfer_signed2)
DoubleSpend: input `{'cid': 0, 'txid': '933cd83a419d2735822a2154c84176a2f419cbd449a74b94e592ab807af23861'}` was already spent

see https://github.com/bigchaindb/bigchaindb/blob/master/docs/source/drivers-clients/python-server-api-examples.md#double-spends

arunkumarsekar commented 7 years ago

Here we go... Hand crafted Hash-Locked ...

import hashlib import json import time

from bigchaindb_driver import BigchainDB import cryptoconditions as cc from cryptoconditions.crypto import Ed25519SigningKey as SigningKey

Create Transaction

class CreateTxnManager():

def __init__(self, params):
    self.bigchaindb_url = 'http://172.16.217.94:9984'
    self.b = self.getChainDriver()
    self.condition = ''
    self.owner_pub_key = params.get('owner_public_key')
    self.owner_priv_key = params.get('owner_private_key')
    self.requester_pub_key = params.get('requester_publickey')

def getChainDriver(self):
    return BigchainDB(self.bigchaindb_url)

def txInput(self):
    input_ = {
        'fulfillment': None,
        'fulfills': None,
        'owners_before': (self.owner_pub_key,)
    }
    return input_

def txOutput(self):
    secret = str.encode(self.secret_message)
    tx_condition = cc.PreimageSha256Fulfillment(preimage=secret)
    self.condition = tx_condition
    output = {
        'amount': 1,
        'condition': {
            'details': tx_condition.to_dict(),
            'uri': tx_condition.condition_uri,
        },
        'public_keys': (self.owner_pub_key,),
    }
    return output

def tx_prepare(self, assets):
    return {
        'operation': 'CREATE',
        'asset': {
            'data': assets
        },
        'metadata': None,
        'outputs': (self.txOutput(),),
        'inputs': (self.txInput(),),
        'version': '0.9',
    }

def createAsset(self, params, assets):
    key = str(time.time())
    self.secret_message = hashlib.sha3_256(key.encode()).hexdigest()
    return self.deployAsset(assets)

def deployAsset(self, assets):
    tx = self.tx_prepare(assets)
    json_str_tx = json.dumps(
        tx,
        sort_keys=True,
        separators=(',', ':'),
        ensure_ascii=False,
    )
    creation_txid = hashlib.sha3_256(json_str_tx.encode()).hexdigest()
    tx['id'] = creation_txid
    fl_str_tx = json.dumps(
        tx,
        sort_keys=True,
        separators=(',', ':'),
        ensure_ascii=False,
    )
    tx['inputs'][0]['fulfillment'] = self.condition.serialize_uri()
    returned_creation_tx = self.b.transactions.send(tx)

    if self.validateTx(tx['id']) == 'valid':
        return {'status':'True', "token": self.secret_message, "create_txn_id": tx['id'] }
    else:
        return {'status':'False'}

def validateTx(self, txid):
    trails = 0
    while trails < 100:
        try:
            if self.b.transactions.status(txid).get('status') == 'valid':
                break
        except bigchaindb_driver.exceptions.NotFoundError:
            trails += 1
    return self.b.transactions.status(txid).get('status')

Transfer txn

class TranferTxManager():

# token : secret_token
def __init__(self, token):
    self.bigchaindb_url = 'http://172.16.217.94:9984'
    self.b = self.getChainDriver()
    self.preimage = token

def getChainDriver(self):
    return BigchainDB(self.bigchaindb_url)

def validateTx(self, txid):
    trails = 0
    while trails < 100:
        try:
            if self.b.transactions.status(txid).get('status') == 'valid':
                break
        except bigchaindb_driver.exceptions.NotFoundError:
            trails += 1
    return self.b.transactions.status(txid).get('status')
"""
data = {
    'txnId': <Create TXN ID>,
    'pubkey': "<requester pub key>"
}
"""
def trasferAsset(self, data):
    data = self.getTxnDetails()
    if data and data.get('txId'):
        if self.validateTx(data.get('txId')) == 'valid':
            return self.prepareAssetTransfer(data)
        else:
            return {"status":"False", "error": "invalid create transaction"}
    return {"status":"False", "error": "invalid attempt"}

def transferInput(self, tx):
    index = 0
    output = tx['outputs'][index]
    input_ = {
      'fulfillment': output['condition']['details'],
      'fulfills': {
          'output': index,
          'txid': tx['id'],
      },
      'owners_before': output['public_keys'],
    }
    return input_

def prepareAssetTransfer(self, data):
    txnid = data.get('txId')
    tx = self.b.transactions.retrieve(txnid)
    transferAsset = {
      'id': tx['id']
    }
    transferTx = self.b.transactions.prepare(
      operation='TRANSFER',
      recipients=data.get('pubkey'),
      asset=transferAsset,
      inputs=self.transferInput(tx),
    )
    transfer_tx_condition = cc.PreimageSha256Fulfillment(preimage=str.encode(self.preimage))
    if tx['inputs'][0]['fulfillment'] == transfer_tx_condition.serialize_uri():
        transferTx['inputs'][0]['fulfillment'] = transfer_tx_condition.serialize_uri()
        sent_transfer_tx = self.b.transactions.send(transferTx)
        return {"status":"True", "asset":tx['asset']['data']}
    else:
        return {"status":"False", "error": "invalid transaction id"}
sbellem commented 7 years ago

Hi @arunkumarsekar! Sorry for not getting back to you earlier. Thanks for sharing your solution! We will look into how we can integrate it soon.

StatelessToken commented 3 years ago

What happened with this? The Docs lead here in a Todo box.

Is the above solution functional at present?