def validate_transactions(self): """ Checks if the incoming transactions are valid """ # create a bigchain instance b = Bigchain() while True: self.monitor.gauge('tx_queue_gauge', self.q_tx_to_validate.qsize(), rate=bigchaindb.config['statsd']['rate']) tx = self.q_tx_to_validate.get() # poison pill if tx == 'stop': self.q_tx_delete.put('stop') self.q_tx_validated.put('stop') return self.q_tx_delete.put(tx['_id']) with self.monitor.timer('validate_transaction', rate=bigchaindb.config['statsd']['rate']): is_valid_transaction = b.is_valid_transaction(tx) if is_valid_transaction: self.q_tx_validated.put(tx)
def validate_transactions(self): """ Checks if the incoming transactions are valid """ # create a bigchain instance b = Bigchain() while True: self.monitor.gauge('tx_queue_gauge', self.q_tx_to_validate.qsize(), rate=bigchaindb.config['statsd']['rate']) tx = self.q_tx_to_validate.get() # poison pill if tx == 'stop': self.q_tx_delete.put('stop') self.q_tx_validated.put('stop') return self.q_tx_delete.put(tx['id']) with self.monitor.timer('validate_transaction', rate=bigchaindb.config['statsd']['rate']): is_valid_transaction = b.is_valid_transaction(tx) if is_valid_transaction: self.q_tx_validated.put(tx)
def validate_transactions(self): """ Checks if the incoming transactions are valid """ # create a bigchain instance b = Bigchain() while True: tx = self.q_tx_to_validate.get() # poison pill if tx == 'stop': self.q_tx_delete.put('stop') self.q_tx_validated.put('stop') return self.q_tx_delete.put(tx['id']) if b.is_valid_transaction(tx): self.q_tx_validated.put(tx)
class Vote: """This class encapsulates the logic to vote on blocks. Note: Methods of this class will be executed in different processes. """ def __init__(self): """Initialize the Block voter.""" # Since cannot share a connection to RethinkDB using multiprocessing, # we need to create a temporary instance of BigchainDB that we use # only to query RethinkDB self.consensus = BaseConsensusRules # This is the Bigchain instance that will be "shared" (aka: copied) # by all the subprocesses self.bigchain = Bigchain() self.last_voted_id = Bigchain().get_last_voted_block().id self.counters = Counter() self.validity = {} self.invalid_dummy_tx = Transaction.create([self.bigchain.me], [self.bigchain.me]) def validate_block(self, block): if not self.bigchain.has_previous_vote(block['id'], block['block']['voters']): try: block = Block.from_dict(block) except (exceptions.InvalidHash, exceptions.InvalidSignature): # XXX: if a block is invalid we should skip the `validate_tx` # step, but since we are in a pipeline we cannot just jump to # another function. Hackish solution: generate an invalid # transaction and propagate it to the next steps of the # pipeline. return block['id'], [self.invalid_dummy_tx] try: self.consensus.validate_block(self.bigchain, block) except (exceptions.InvalidHash, exceptions.OperationError, exceptions.InvalidSignature): # XXX: if a block is invalid we should skip the `validate_tx` # step, but since we are in a pipeline we cannot just jump to # another function. Hackish solution: generate an invalid # transaction and propagate it to the next steps of the # pipeline. return block.id, [self.invalid_dummy_tx] return block.id, block.transactions def ungroup(self, block_id, transactions): """Given a block, ungroup the transactions in it. Args: block_id (str): the id of the block in progress. transactions (list(Transaction)): transactions of the block in progress. Returns: ``None`` if the block has been already voted, an iterator that yields a transaction, block id, and the total number of transactions contained in the block otherwise. """ num_tx = len(transactions) for tx in transactions: yield tx, block_id, num_tx def validate_tx(self, tx, block_id, num_tx): """Validate a transaction. Args: tx (dict): the transaction to validate block_id (str): the id of block containing the transaction num_tx (int): the total number of transactions to process Returns: Three values are returned, the validity of the transaction, ``block_id``, ``num_tx``. """ return bool(self.bigchain.is_valid_transaction(tx)), block_id, num_tx def vote(self, tx_validity, block_id, num_tx): """Collect the validity of transactions and cast a vote when ready. Args: tx_validity (bool): the validity of the transaction block_id (str): the id of block containing the transaction num_tx (int): the total number of transactions to process Returns: None, or a vote if a decision has been reached. """ self.counters[block_id] += 1 self.validity[block_id] = tx_validity and self.validity.get(block_id, True) if self.counters[block_id] == num_tx: vote = self.bigchain.vote(block_id, self.last_voted_id, self.validity[block_id]) self.last_voted_id = block_id del self.counters[block_id] del self.validity[block_id] return vote def write_vote(self, vote): """Write vote to the database. Args: vote: the vote to write. """ self.bigchain.write_vote(vote) return vote
class Block: """This class encapsulates the logic to create blocks. Note: Methods of this class will be executed in different processes. """ def __init__(self): """Initialize the Block creator""" self.bigchain = Bigchain() self.txs = [] def filter_tx(self, tx): """Filter a transaction. Args: tx (dict): the transaction to process. Returns: The transaction if assigned to the current node, ``None`` otherwise. """ if tx['assignee'] == self.bigchain.me: tx.pop('assignee') tx.pop('assignment_timestamp') return tx def validate_tx(self, tx): """Validate a transaction. Also checks if the transaction already exists in the blockchain. If it does, or it's invalid, it's deleted from the backlog immediately. Args: tx (dict): the transaction to validate. Returns: The transaction if valid, ``None`` otherwise. """ if self.bigchain.transaction_exists(tx['id']): # if the transaction already exists, we must check whether # it's in a valid or undecided block tx, status = self.bigchain.get_transaction(tx['id'], include_status=True) if status == self.bigchain.TX_VALID \ or status == self.bigchain.TX_UNDECIDED: # if the tx is already in a valid or undecided block, # then it no longer should be in the backlog, or added # to a new block. We can delete and drop it. r.table('backlog').get(tx['id']) \ .delete(durability='hard') \ .run(self.bigchain.conn) return None tx_validated = self.bigchain.is_valid_transaction(tx) if tx_validated: return tx else: # if the transaction is not valid, remove it from the # backlog r.table('backlog').get(tx['id']) \ .delete(durability='hard') \ .run(self.bigchain.conn) return None def create(self, tx, timeout=False): """Create a block. This method accumulates transactions to put in a block and outputs a block when one of the following conditions is true: - the size limit of the block has been reached, or - a timeout happened. Args: tx (dict): the transaction to validate, might be None if a timeout happens. timeout (bool): ``True`` if a timeout happened (Default: ``False``). Returns: The block, if a block is ready, or ``None``. """ if tx: self.txs.append(tx) if len(self.txs) == 1000 or (timeout and self.txs): block = self.bigchain.create_block(self.txs) self.txs = [] return block def write(self, block): """Write the block to the Database. Args: block (dict): the block of transactions to write to the database. Returns: The block. """ logger.info('Write new block %s with %s transactions', block['id'], len(block['block']['transactions'])) self.bigchain.write_block(block) return block def delete_tx(self, block): """Delete transactions. Args: block (dict): the block containg the transactions to delete. Returns: The block. """ r.table('backlog')\ .get_all(*[tx['id'] for tx in block['block']['transactions']])\ .delete(durability='hard')\ .run(self.bigchain.conn) return block
class Vote: """This class encapsulates the logic to vote on blocks. Note: Methods of this class will be executed in different processes. """ def __init__(self): """Initialize the Block voter.""" # Since cannot share a connection to RethinkDB using multiprocessing, # we need to create a temporary instance of BigchainDB that we use # only to query RethinkDB self.consensus = BaseConsensusRules # This is the Bigchain instance that will be "shared" (aka: copied) # by all the subprocesses self.bigchain = Bigchain() self.last_voted_id = Bigchain().get_last_voted_block().id self.counters = Counter() self.validity = {} self.invalid_dummy_tx = Transaction.create([self.bigchain.me], [([self.bigchain.me], 1)]) def validate_block(self, block): if not self.bigchain.has_previous_vote(block['id'], block['block']['voters']): try: block = Block.from_dict(block) except (exceptions.InvalidHash, exceptions.InvalidSignature): # XXX: if a block is invalid we should skip the `validate_tx` # step, but since we are in a pipeline we cannot just jump to # another function. Hackish solution: generate an invalid # transaction and propagate it to the next steps of the # pipeline. return block['id'], [self.invalid_dummy_tx] try: self.consensus.validate_block(self.bigchain, block) except (exceptions.InvalidHash, exceptions.OperationError, exceptions.InvalidSignature): # XXX: if a block is invalid we should skip the `validate_tx` # step, but since we are in a pipeline we cannot just jump to # another function. Hackish solution: generate an invalid # transaction and propagate it to the next steps of the # pipeline. return block.id, [self.invalid_dummy_tx] return block.id, block.transactions def ungroup(self, block_id, transactions): """Given a block, ungroup the transactions in it. Args: block_id (str): the id of the block in progress. transactions (list(Transaction)): transactions of the block in progress. Returns: ``None`` if the block has been already voted, an iterator that yields a transaction, block id, and the total number of transactions contained in the block otherwise. """ num_tx = len(transactions) for tx in transactions: yield tx, block_id, num_tx def validate_tx(self, tx, block_id, num_tx): """Validate a transaction. Args: tx (dict): the transaction to validate block_id (str): the id of block containing the transaction num_tx (int): the total number of transactions to process Returns: Three values are returned, the validity of the transaction, ``block_id``, ``num_tx``. """ return bool(self.bigchain.is_valid_transaction(tx)), block_id, num_tx def vote(self, tx_validity, block_id, num_tx): """Collect the validity of transactions and cast a vote when ready. Args: tx_validity (bool): the validity of the transaction block_id (str): the id of block containing the transaction num_tx (int): the total number of transactions to process Returns: None, or a vote if a decision has been reached. """ self.counters[block_id] += 1 self.validity[block_id] = tx_validity and self.validity.get(block_id, True) if self.counters[block_id] == num_tx: vote = self.bigchain.vote(block_id, self.last_voted_id, self.validity[block_id]) self.last_voted_id = block_id del self.counters[block_id] del self.validity[block_id] return vote def write_vote(self, vote): """Write vote to the database. Args: vote: the vote to write. """ self.bigchain.write_vote(vote) return vote
testuser1_priv, testuser1_pub = crypto.generate_key_pair() print("testuser1_priv:" + testuser1_priv) print("testuser1_pub:" + testuser1_pub) payload = { "msg": "first charge for user A", "issue": "charge", "category": "currency", "amount": 300, "asset": "", "account": 0, "previous": "genesis", "trader": "" } tx = b.create_transaction(b.me, testuser1_pub, None, 'CREATE', payload=payload) tx_signed = b.sign_transaction(tx, b.me_private) if b.is_valid_transaction(tx_signed): b.write_transaction(tx_signed) print(tx_signed) # user B testuser2_priv, testuser2_pub = crypto.generate_key_pair() print("testuser2_priv:" + testuser2_priv) print("testuser2_pub:" + testuser2_pub) payload2 = { "msg": "first charge for user B", "issue": "charge", "category": "currency", "amount": 400, "asset": "", "account": 0, "previous": "genesis",
class Block: """This class encapsulates the logic to create blocks. Note: Methods of this class will be executed in different processes. """ def __init__(self): """Initialize the Block creator""" self.bigchain = Bigchain() self.txs = [] def filter_tx(self, tx): """Filter a transaction. Args: tx (dict): the transaction to process. Returns: The transaction if assigned to the current node, ``None`` otherwise. """ if tx['assignee'] == self.bigchain.me: tx.pop('assignee') return tx def delete_tx(self, tx): """Delete a transaction. Args: tx (dict): the transaction to delete. Returns: The transaction. """ r.table('backlog')\ .get(tx['id'])\ .delete(durability='hard')\ .run(self.bigchain.conn) return tx def validate_tx(self, tx): """Validate a transaction. Args: tx (dict): the transaction to validate. Returns: The transaction if valid, ``None`` otherwise. """ tx = self.bigchain.is_valid_transaction(tx) if tx: return tx def create(self, tx, timeout=False): """Create a block. This method accumulates transactions to put in a block and outputs a block when one of the following conditions is true: - the size limit of the block has been reached, or - a timeout happened. Args: tx (dict): the transaction to validate, might be None if a timeout happens. timeout (bool): ``True`` if a timeout happened (Default: ``False``). Returns: The block, if a block is ready, or ``None``. """ if tx: self.txs.append(tx) if len(self.txs) == 1000 or (timeout and self.txs): block = self.bigchain.create_block(self.txs) self.txs = [] return block def write(self, block): """Write the block to the Database. Args: block (dict): the block of transactions to write to the database. Returns: The block. """ logger.info('Write new block %s with %s transactions', block['id'], len(block['block']['transactions'])) self.bigchain.write_block(block) return block
payload_A = { "msg" : "node send -50 to A,is -50", "issue" : "cost", "category" : "currency", "amount" : 50, "asset":"", # final owner 's account "account":300, "previous":testuser1_last['txid'], "trader":testuser2_pub } tx_create= b.create_transaction(b.me, testuser1_pub, None, 'CREATE', payload=payload_A) tx_create_signed = b.sign_transaction(tx_create, b.me_private) if b.is_valid_transaction(tx_create_signed): b.write_transaction(tx_create_signed) # node create transaction for b, testuser2_last=b.get_owned_ids(testuser2_pub).pop() payload_B = { "msg" : "node send +50 to B,is +50", "issue" : "earn", "category" : "currency", "amount" : 50, "asset":"", # final owner 's account "account":400, "previous":testuser2_last['txid'], "trader":testuser1_pub }
'condition': { 'uri': first_tx_condition.condition.serialize_uri() }, 'cid': 0, 'new_owners': None }) # Conditions have been updated, so hash needs updating hashlock_tx['id'] = util.get_hash_data(hashlock_tx) # The asset needs to be signed by the current_owner hashlock_tx_signed = b.sign_transaction(hashlock_tx, b.me_private) # Some validations assert b.validate_transaction(hashlock_tx_signed) == hashlock_tx_signed assert b.is_valid_transaction(hashlock_tx_signed) == hashlock_tx_signed b.write_transaction(hashlock_tx_signed) print(json.dumps(hashlock_tx_signed, sort_keys=True, indent=4, separators=(',', ':'))) sleep(8) hashlockuser_priv, hashlockuser_pub = crypto.generate_key_pair() # create hashlock fulfillment tx hashlock_fulfill_tx = b.create_transaction(None, hashlockuser_priv, {'txid': hashlock_tx['id'], 'cid': 0}, 'TRANSFER') # try a wrong secret hashlock_fulfill_tx_fulfillment = cc.PreimageSha256Fulfillment(preimage=b'') hashlock_fulfill_tx['transaction']['fulfillments'][0]['fulfillment'] = \ hashlock_fulfill_tx_fulfillment.serialize_uri()
testuser1_priv, testuser1_pub = crypto.generate_key_pair() print("testuser1_priv:"+testuser1_priv) print("testuser1_pub:"+testuser1_pub) payload = { "msg" : "first charge for user A", "issue" : "charge", "category" : "currency", "amount" : 300, "asset":"", "account":0, "previous":"genesis", "trader":"" } tx = b.create_transaction(b.me, testuser1_pub, None, 'CREATE', payload=payload) tx_signed = b.sign_transaction(tx, b.me_private) if b.is_valid_transaction(tx_signed): b.write_transaction(tx_signed) print(tx_signed) # user B testuser2_priv, testuser2_pub = crypto.generate_key_pair() print("testuser2_priv:"+testuser2_priv) print("testuser2_pub:"+testuser2_pub) payload2 = { "msg" : "first charge for user B", "issue" : "charge", "category" : "currency", "amount" : 400, "asset":"", "account":0,
class ChainQuery(object): def __init__(self, host=None, port=None, dbname=None,pub_key=None,priv_key=None,keyring=[], consensus_plugin=None): self.host = host self.port = port self.dbname = dbname self.conn =r.connect(host=host,port=port,db=dbname) self.bigchain=Bigchain(host=host,port=port,dbname=dbname,public_key=pub_key,private_key=priv_key,keyring=keyring,consensus_plugin=consensus_plugin) #test def test(self): tables=r.db('bigchain').table_list().run(self.conn) #print(tables) return tables # create key_pair for user def generate_key_pair(self): return crypto.generate_key_pair() # create asset def create_asset(self,public_key, digital_asset_payload): tx = self.bigchain.create_transaction(self.bigchain.me, public_key, None, 'CREATE', payload=digital_asset_payload) tx_signed = self.bigchain.sign_transaction(tx, self.bigchain.me_private) return self.bigchain.write_transaction(tx_signed) # get transaction by payload_uuid def getTxid_by_payload_uuid(self,payload_uuid): cursor = r.table('bigchain') \ .get_all(payload_uuid, index='payload_uuid') \ .pluck({'block':{'transactions':'id'}}) \ .run(self.conn) transactions = list(cursor) return transactions # get transaction by payload def getTxid_by_payload(self,payload): pass # get currentowner of a payload(assert) def getOwnerofAssert(self,payload): return # get one's assert def get_owned_asserts(self,pub_key): return # if tx contains someone def tx_contains_one(self,tx,one_pub): for condition in tx['conditions']: if one_pub in condition['new_owners']: return True for fullfillment in tx['fulfillments']: if one_pub in fullfillment['current_owners']: return True # transfer assert to another, old_owner create this transaction,so need old_owner's pub/priv key. def transfer_assert(self,old_owner_pub,old_owner_priv,new_owner_pub,tx_id): tx_transfer=self.bigchain.create_transaction(old_owner_pub,new_owner_pub,tx_id,'TRANSFER') tx_transfer_signed=self.bigchain.sign_transaction(tx_transfer,old_owner_priv) #check if the transaction is valid check=self.bigchain.is_valid_transaction(tx_transfer_signed) if check: self.bigchain.write_transaction(tx_transfer_signed) else: logger.info('this transaction is invalid.')
class BlockPipeline: """This class encapsulates the logic to create blocks. Note: Methods of this class will be executed in different processes. """ def __init__(self): """Initialize the BlockPipeline creator""" self.bigchain = Bigchain() self.txs = [] def filter_tx(self, tx): """Filter a transaction. Args: tx (dict): the transaction to process. Returns: dict: The transaction if assigned to the current node, ``None`` otherwise. """ if tx['assignee'] == self.bigchain.me: tx.pop('assignee') tx.pop('assignment_timestamp') return tx def validate_tx(self, tx): """Validate a transaction. Also checks if the transaction already exists in the blockchain. If it does, or it's invalid, it's deleted from the backlog immediately. Args: tx (dict): the transaction to validate. Returns: :class:`~bigchaindb.models.Transaction`: The transaction if valid, ``None`` otherwise. """ try: tx = Transaction.from_dict(tx) except (SchemaValidationError, InvalidHash, InvalidSignature, AmountError): return None # If transaction is in any VALID or UNDECIDED block we # should not include it again if not self.bigchain.is_new_transaction(tx.id): self.bigchain.delete_transaction(tx.id) return None # If transaction is not valid it should not be included if not self.bigchain.is_valid_transaction(tx): self.bigchain.delete_transaction(tx.id) return None return tx def create(self, tx, timeout=False): """Create a block. This method accumulates transactions to put in a block and outputs a block when one of the following conditions is true: - the size limit of the block has been reached, or - a timeout happened. Args: tx (:class:`~bigchaindb.models.Transaction`): the transaction to validate, might be None if a timeout happens. timeout (bool): ``True`` if a timeout happened (Default: ``False``). Returns: :class:`~bigchaindb.models.Block`: The block, if a block is ready, or ``None``. """ if tx: self.txs.append(tx) if len(self.txs) == 1000 or (timeout and self.txs): block = self.bigchain.create_block(self.txs) self.txs = [] return block def write(self, block): """Write the block to the Database. Args: block (:class:`~bigchaindb.models.Block`): the block of transactions to write to the database. Returns: :class:`~bigchaindb.models.Block`: The Block. """ logger.info('Write new block %s with %s transactions', block.id, len(block.transactions)) self.bigchain.write_block(block) return block def delete_tx(self, block): """Delete transactions. Args: block (:class:`~bigchaindb.models.Block`): the block containg the transactions to delete. Returns: :class:`~bigchaindb.models.Block`: The block. """ self.bigchain.delete_transaction(*[tx.id for tx in block.transactions]) return block
class ChainQuery(object): def __init__( self, host=None, port=None, dbname=None, pub_key=None, priv_key=None, keyring=[], consensus_plugin=None ): self.host = host self.port = port self.dbname = dbname self.conn = r.connect(host=host, port=port, db=dbname) self.bigchain = Bigchain( host=host, port=port, dbname=dbname, public_key=pub_key, private_key=priv_key, keyring=keyring, consensus_plugin=consensus_plugin, ) # test def test(self): tables = r.db("bigchain").table_list().run(self.conn) # print(tables) return tables # create key_pair for user def generate_key_pair(self): return crypto.generate_key_pair() # create asset def create_asset(self, public_key, digital_asset_payload): tx = self.bigchain.create_transaction( self.bigchain.me, public_key, None, "CREATE", payload=digital_asset_payload ) tx_signed = self.bigchain.sign_transaction(tx, self.bigchain.me_private) return self.bigchain.write_transaction(tx_signed) # get transaction by payload_uuid def getTxid_by_payload_uuid(self, payload_uuid): cursor = ( r.table("bigchain") .get_all(payload_uuid, index="payload_uuid") .pluck({"block": {"transactions": "id"}}) .run(self.conn) ) transactions = list(cursor) return transactions # get transaction by payload def getTxid_by_payload(self, payload): pass # get currentowner of a payload(assert) def getOwnerofAssert(self, payload): return # get one's assert def get_owned_asserts(self, pub_key): return # if tx contains someone def tx_contains_one(self, tx, one_pub): for condition in tx["conditions"]: if one_pub in condition["new_owners"]: return True for fullfillment in tx["fulfillments"]: if one_pub in fullfillment["current_owners"]: return True # transfer assert to another, old_owner create this transaction,so need old_owner's pub/priv key. def transfer_assert(self, old_owner_pub, old_owner_priv, new_owner_pub, tx_id): tx_transfer = self.bigchain.create_transaction(old_owner_pub, new_owner_pub, tx_id, "TRANSFER") tx_transfer_signed = self.bigchain.sign_transaction(tx_transfer, old_owner_priv) # check if the transaction is valid check = self.bigchain.is_valid_transaction(tx_transfer_signed) if check: self.bigchain.write_transaction(tx_transfer_signed) else: logger.info("this transaction is invalid.")