def vote(self, block_id, previous_block_id, decision, invalid_reason=None): """Create a signed vote for a block given the :attr:`previous_block_id` and the :attr:`decision` (valid/invalid). Args: block_id (str): The id of the block to vote on. previous_block_id (str): The id of the previous block. decision (bool): Whether the block is valid or invalid. invalid_reason (Optional[str]): Reason the block is invalid """ if block_id == previous_block_id: raise exceptions.CyclicBlockchainError() vote = { 'voting_for_block': block_id, 'previous_block': previous_block_id, 'is_block_valid': decision, 'invalid_reason': invalid_reason, 'timestamp': gen_timestamp() } vote_data = serialize(vote) signature = crypto.PrivateKey(self.me_private).sign(vote_data.encode()) vote_signed = { 'node_pubkey': self.me, 'signature': signature.decode(), 'vote': vote } return vote_signed
def vote(self, block_id, previous_block_id, decision, invalid_reason=None): """Create a signed vote for a block given the :attr:`previous_block_id` and the :attr:`decision` (valid/invalid). Args: block_id (str): The id of the block to vote on. previous_block_id (str): The id of the previous block. decision (bool): Whether the block is valid or invalid. invalid_reason (Optional[str]): Reason the block is invalid """ if block_id == previous_block_id: raise exceptions.CyclicBlockchainError() vote = { 'voting_for_block': block_id, 'previous_block': previous_block_id, 'is_block_valid': decision, 'invalid_reason': invalid_reason, 'timestamp': gen_timestamp() } vote_data = serialize(vote) signature = crypto.PrivateKey(self.me_private).sign(vote_data.encode()) vote_signed = { 'node_pubkey': self.me, 'signature': signature.decode(), 'vote': vote } return vote_signed
def test_block_deserialization(self, b): from bigchaindb.common.crypto import hash_data from bigchaindb.common.utils import gen_timestamp, serialize from bigchaindb.models import Block, Transaction transaction = Transaction.create([b.me], [([b.me], 1)]) transaction.sign([b.me_private]) timestamp = gen_timestamp() voters = ['Qaaa', 'Qbbb'] expected = Block([transaction], b.me, timestamp, voters) block = { 'timestamp': timestamp, 'transactions': [transaction.to_dict()], 'node_pubkey': b.me, 'voters': voters, } block_body = { 'id': hash_data(serialize(block)), 'block': block, 'signature': None, } assert expected == Block.from_dict(block_body)
def __init__(self, transactions=None, node_pubkey=None, timestamp=None, voters=None, signature=None): """The Block model is mainly used for (de)serialization and integrity checking. Args: transaction (:obj:`list` of :class:`~.Transaction`): Transactions to be included in the Block. node_pubkey (str): The public key of the node creating the Block. timestamp (str): The Unix time a Block was created. voters (:obj:`list` of :obj:`str`): A list of a federation nodes' public keys supposed to vote on the Block. signature (str): A cryptographic signature ensuring the integrity and validity of the creator of a Block. """ if transactions is not None and not isinstance(transactions, list): raise TypeError('`transactions` must be a list instance or None') else: self.transactions = transactions or [] if voters is not None and not isinstance(voters, list): raise TypeError('`voters` must be a list instance or None') else: self.voters = voters or [] if timestamp is not None: self.timestamp = timestamp else: self.timestamp = gen_timestamp() self.node_pubkey = node_pubkey self.signature = signature
def __init__(self, transactions=None, node_pubkey=None, timestamp=None, signature=None): """The Block model is mainly used for (de)serialization and integrity checking. Args: transaction (:obj:`list` of :class:`~.Transaction`): Transactions to be included in the Block. node_pubkey (str): The public key of the node creating the Block. timestamp (str): The Unix time a Block was created. signature (str): A cryptographic signature ensuring the integrity and validity of the creator of a Block. """ if transactions is not None and not isinstance(transactions, list): raise TypeError('`transactions` must be a list instance or None') else: self.transactions = transactions or [] if timestamp is not None: self.timestamp = timestamp else: self.timestamp = gen_timestamp() self.node_pubkey = node_pubkey self.signature = signature
def test_block_invalid_signature(self, b, alice): from bigchaindb.common.crypto import hash_data from bigchaindb.common.exceptions import InvalidSignature from bigchaindb.common.utils import gen_timestamp, serialize from bigchaindb.models import Block, Transaction transaction = Transaction.create([alice.public_key], [([alice.public_key], 1)]) transaction.sign([alice.private_key]) timestamp = gen_timestamp() block = { 'timestamp': timestamp, 'transactions': [transaction.to_dict()], 'node_pubkey': alice.public_key, } block_body = { 'id': hash_data(serialize(block)), 'block': block, 'signature': 'an invalid signature', } with raises(InvalidSignature): Block.from_dict(block_body).validate(b)
def test_block_serialization(self, b, alice): from bigchaindb.common.crypto import hash_data from bigchaindb.common.utils import gen_timestamp, serialize from bigchaindb.models import Block, Transaction transactions = [ Transaction.create([alice.public_key], [([alice.public_key], 1)]) ] timestamp = gen_timestamp() voters = ['Qaaa', 'Qbbb'] expected_block = { 'timestamp': timestamp, 'transactions': [tx.to_dict() for tx in transactions], 'node_pubkey': alice.public_key, 'voters': voters, } expected = { 'id': hash_data(serialize(expected_block)), 'block': expected_block, 'signature': None, } block = Block(transactions, alice.public_key, timestamp, voters) assert block.to_dict() == expected
def test_sign_block(self, b, alice): from bigchaindb.common.crypto import PrivateKey, PublicKey from bigchaindb.common.utils import gen_timestamp, serialize from bigchaindb.models import Block, Transaction transactions = [ Transaction.create([alice.public_key], [([alice.public_key], 1)]) ] timestamp = gen_timestamp() voters = ['Qaaa', 'Qbbb'] expected_block = { 'timestamp': timestamp, 'transactions': [tx.to_dict() for tx in transactions], 'node_pubkey': alice.public_key, 'voters': voters, } expected_block_serialized = serialize(expected_block).encode() expected = PrivateKey( alice.private_key).sign(expected_block_serialized) block = Block(transactions, alice.public_key, timestamp, voters) block = block.sign(alice.private_key) assert block.signature == expected.decode() public_key = PublicKey(alice.public_key) assert public_key.verify(expected_block_serialized, block.signature)
def connect_and_recv(event_queue): session = aiohttp.ClientSession() ws = yield from session.ws_connect(URL) logger.info('Connected to tendermint ws server') stream_id = 'bigchaindb_stream_{}'.format(gen_timestamp()) yield from subscribe_events(ws, stream_id) while True: msg = yield from ws.receive() process_event(event_queue, msg.data, stream_id) if msg.type in (aiohttp.WSMsgType.CLOSED, aiohttp.WSMsgType.ERROR): session.close() raise aiohttp.ClientConnectionError()
def create_stream(self): tx_create = self.bdb.transactions.prepare( operation='CREATE', signers=self.sharge.public_key, asset={ 'data': { 'mobile': self.mobile.public_key, 'timestamp': gen_timestamp() } }, ) tx_create_signed = self.bdb.transactions.fulfill(tx_create, private_keys=self.sharge.private_key) self.bdb.transactions.send(tx_create_signed) poll_status_and_fetch_transaction(tx_create_signed['id'], driver=self.bdb) self.data_stream.append(tx_create_signed['id'])
def connect_and_recv(event_queue): session = aiohttp.ClientSession() ws = yield from session.ws_connect(URL) logger.info('Connected to tendermint ws server') stream_id = 'bigchaindb_stream_{}'.format(gen_timestamp()) yield from subscribe_events(ws, stream_id) while True: msg = yield from ws.receive() process_event(event_queue, msg.data, stream_id) if msg.type in (aiohttp.WSMsgType.CLOSED, aiohttp.WSMsgType.ERROR): session.close() raise aiohttp.ClientConnectionError()
def _input_valid(input_, operation, tx_serialized, output_condition_uri=None): """Validates a single Input against a single Output. Note: In case of a `CREATE` or `GENESIS` Transaction, this method does not validate against `output_condition_uri`. Args: input_ (:class:`~bigchaindb.common.transaction. Input`) The Input to be signed. operation (str): The type of Transaction. tx_serialized (str): The Transaction used as a message when initially signing it. output_condition_uri (str, optional): An Output to check the Input against. Returns: bool: If the Input is valid. """ ccffill = input_.fulfillment try: parsed_ffill = Fulfillment.from_uri(ccffill.serialize_uri()) except (TypeError, ValueError, ParsingError): return False if operation in (Transaction.CREATE, Transaction.GENESIS): # NOTE: In the case of a `CREATE` or `GENESIS` transaction, the # output is always valid. output_valid = True else: output_valid = output_condition_uri == ccffill.condition_uri # NOTE: We pass a timestamp to `.validate`, as in case of a timeout # condition we'll have to validate against it # cryptoconditions makes no assumptions of the encoding of the # message to sign or verify. It only accepts bytestrings ffill_valid = parsed_ffill.validate(message=tx_serialized.encode(), now=gen_timestamp()) return output_valid and ffill_valid
def _input_valid(input_, operation, tx_serialized, output_condition_uri=None): """Validates a single Input against a single Output. Note: In case of a `CREATE` or `GENESIS` Transaction, this method does not validate against `output_condition_uri`. Args: input_ (:class:`~bigchaindb.common.transaction. Input`) The Input to be signed. operation (str): The type of Transaction. tx_serialized (str): The Transaction used as a message when initially signing it. output_condition_uri (str, optional): An Output to check the Input against. Returns: bool: If the Input is valid. """ ccffill = input_.fulfillment try: parsed_ffill = Fulfillment.from_uri(ccffill.serialize_uri()) except (TypeError, ValueError, ParsingError): return False if operation in (Transaction.CREATE, Transaction.GENESIS): # NOTE: In the case of a `CREATE` or `GENESIS` transaction, the # output is always valid. output_valid = True else: output_valid = output_condition_uri == ccffill.condition_uri # NOTE: We pass a timestamp to `.validate`, as in case of a timeout # condition we'll have to validate against it # cryptoconditions makes no assumptions of the encoding of the # message to sign or verify. It only accepts bytestrings ffill_valid = parsed_ffill.validate(message=tx_serialized.encode(), now=gen_timestamp()) return output_valid and ffill_valid
def create_block(self, validated_transactions): """Creates a block given a list of `validated_transactions`. Note that this method does not validate the transactions. Transactions should be validated before calling create_block. Args: validated_transactions (list(Transaction)): list of validated transactions. Returns: Block: created block. """ # Prevent the creation of empty blocks if not validated_transactions: raise exceptions.OperationError('Empty block creation is not ' 'allowed') voters = list(self.federation) block = Block(validated_transactions, self.me, gen_timestamp(), voters) block = block.sign(self.me_private) return block
def create_block(self, validated_transactions): """Creates a block given a list of `validated_transactions`. Note that this method does not validate the transactions. Transactions should be validated before calling create_block. Args: validated_transactions (list(Transaction)): list of validated transactions. Returns: Block: created block. """ # Prevent the creation of empty blocks if len(validated_transactions) == 0: raise exceptions.OperationError('Empty block creation is not ' 'allowed') voters = self.nodes_except_me + [self.me] block = Block(validated_transactions, self.me, gen_timestamp(), voters) block = block.sign(self.me_private) return block
def test_block_deserialization(self, b, alice): from bigchaindb.common.crypto import hash_data from bigchaindb.common.utils import gen_timestamp, serialize from bigchaindb.models import Block, Transaction transaction = Transaction.create([alice.public_key], [([alice.public_key], 1)]) transaction.sign([alice.private_key]) timestamp = gen_timestamp() expected = Block([transaction], alice.public_key, timestamp) block = { 'timestamp': timestamp, 'transactions': [transaction.to_dict()], 'node_pubkey': alice.public_key, } block_body = { 'id': hash_data(serialize(block)), 'block': block, 'signature': None, } assert expected == Block.from_dict(block_body)
def generate_mock_data(): mobile = generate_keypair() plug = generate_keypair() now = int(gen_timestamp()) data = [] num_samples = 6 for i in range(num_samples + 1): door_control = 1 if i in [0, num_samples] else 0 plugged = 0 if i in [0, num_samples] else 1 charge_control = random.randint(2, 10) data.append({ 'id_plug': plug.public_key, 'id_mobile': mobile.public_key, 'time': str(now + i * 60), 'door_control': str(door_control), 'plugged': str(plugged), 'charge_control_kw': str(charge_control) }) pprint.pprint(data) return data, plug, mobile
def test_block_invalid_signature(self, b): from bigchaindb.common.crypto import hash_data from bigchaindb.common.exceptions import InvalidSignature from bigchaindb.common.utils import gen_timestamp, serialize from bigchaindb.models import Block, Transaction transactions = [Transaction.create([b.me], [([b.me], 1)])] timestamp = gen_timestamp() block = { 'timestamp': timestamp, 'transactions': [tx.to_dict() for tx in transactions], 'node_pubkey': b.me, 'voters': list(b.federation), } block_body = { 'id': hash_data(serialize(block)), 'block': block, 'signature': 'an invalid signature', } with raises(InvalidSignature): Block.from_dict(block_body).validate(b)
def escrow_asset(bigchain, source, to, asset_id, sk, expires_at=None, ilp_header=None, execution_condition=None): asset = bigchain.get_transaction(asset_id['txid']) payload = asset['transaction']['data']['payload'].copy() if ilp_header: payload.update({'ilp_header': ilp_header}) # Create escrow template with the execute and abort address asset_escrow = bigchain.create_transaction(source, [source, to], asset_id, 'TRANSFER', payload=payload) if not expires_at: # Set expiry time (100 secs from now) time_sleep = 100 expires_at = float(gen_timestamp()) + time_sleep # Create escrow and timeout condition condition_escrow = cc.ThresholdSha256Fulfillment(threshold=1) # OR Gate condition_timeout = cc.TimeoutFulfillment( expire_time=str(expires_at)) # only valid if now() <= time_expire condition_timeout_inverted = cc.InvertedThresholdSha256Fulfillment( threshold=1) condition_timeout_inverted.add_subfulfillment(condition_timeout) # Create execute branch execution_threshold = 3 if execution_condition else 2 condition_execute = cc.ThresholdSha256Fulfillment( threshold=execution_threshold) # AND gate condition_execute.add_subfulfillment( cc.Ed25519Fulfillment(public_key=to)) # execute address condition_execute.add_subfulfillment( condition_timeout) # federation checks on expiry if execution_condition: condition_execute.add_subcondition_uri(execution_condition) condition_escrow.add_subfulfillment(condition_execute) # Create abort branch condition_abort = cc.ThresholdSha256Fulfillment(threshold=2) # AND gate condition_abort.add_subfulfillment( cc.Ed25519Fulfillment(public_key=source)) # abort address condition_abort.add_subfulfillment(condition_timeout_inverted) condition_escrow.add_subfulfillment(condition_abort) # Update the condition in the newly created transaction asset_escrow['transaction']['conditions'][0]['condition'] = { 'details': condition_escrow.to_dict(), 'uri': condition_escrow.condition.serialize_uri() } # conditions have been updated, so hash needs updating asset_escrow['id'] = crypto.hash_data(asset_escrow) # sign transaction asset_escrow_signed = bigchaindb.util.sign_tx(asset_escrow, sk, bigchain=bigchain) bigchain.write_transaction(asset_escrow_signed) return asset_escrow_signed