def test_can_add_node_declaration_block( forced_memory_blockchain: MemoryBlockchain, user_account_key_pair: KeyPair, ): blockchain = forced_memory_blockchain user_account = user_account_key_pair.public request0 = NodeDeclarationSignedChangeRequest.create( network_addresses=['http://127.0.0.1'], fee_amount=3, signing_key=user_account_key_pair.private) block0 = Block.create_from_signed_change_request(blockchain, request0, get_node_signing_key()) blockchain.add_block(block0) assert blockchain.get_node_by_identifier( user_account) == request0.message.node blockchain.snapshot_blockchain_state() assert blockchain.blockchain_states[-1].get_node( user_account) == request0.message.node request1 = NodeDeclarationSignedChangeRequest.create( network_addresses=['http://127.0.0.2', 'http://192.168.0.34'], fee_amount=3, signing_key=user_account_key_pair.private) block1 = Block.create_from_signed_change_request(blockchain, request1, get_node_signing_key()) blockchain.add_block(block1) assert blockchain.get_node_by_identifier( user_account) == request1.message.node blockchain.snapshot_blockchain_state() assert blockchain.blockchain_states[-1].get_node( user_account) == request1.message.node
def test_get_account_lock( forced_memory_blockchain: MemoryBlockchain, treasury_account_key_pair: KeyPair, user_account_key_pair: KeyPair, ): blockchain = forced_memory_blockchain treasury_account = treasury_account_key_pair.public user_account = user_account_key_pair.public assert blockchain.get_next_block_number() == 0 assert blockchain.get_account_current_balance_lock( treasury_account) == treasury_account assert blockchain.get_account_balance_lock(treasury_account, -1) == treasury_account block0 = Block.create_from_main_transaction( blockchain=blockchain, recipient=user_account, amount=30, request_signing_key=treasury_account_key_pair.private, pv_signing_key=get_node_signing_key(), ) blockchain.add_block(block0) assert blockchain.get_next_block_number() == 1 block0_treasury_account_balance = block0.message.updated_account_states.get( treasury_account) assert block0_treasury_account_balance assert blockchain.get_account_current_balance_lock( treasury_account) == block0_treasury_account_balance.balance_lock assert blockchain.get_account_balance_lock(treasury_account, -1) == treasury_account block1 = Block.create_from_main_transaction( blockchain=blockchain, recipient=user_account, amount=10, request_signing_key=treasury_account_key_pair.private, pv_signing_key=get_node_signing_key(), ) blockchain.add_block(block1) assert blockchain.get_next_block_number() == 2 block1_treasury_account_balance = block1.message.updated_account_states.get( treasury_account) assert block1_treasury_account_balance assert blockchain.get_account_current_balance_lock( treasury_account) == block1_treasury_account_balance.balance_lock assert blockchain.get_account_balance_lock( treasury_account, 0) == block0_treasury_account_balance.balance_lock assert blockchain.get_account_balance_lock(treasury_account, -1) == treasury_account
def test_can_create_block_from_signed_change_request( forced_mock_blockchain, sample_signed_change_request: CoinTransferSignedChangeRequest ): sender = sample_signed_change_request.signer assert sender def get_account_balance(self, account, on_block_number): return 450 if account == sender else 0 with patch.object(MockBlockchain, 'get_account_balance', new=get_account_balance): block = Block.create_from_signed_change_request( forced_mock_blockchain, sample_signed_change_request, get_node_signing_key() ) assert block.message assert block.hash assert block.signature block.validate_signature() assert block.signer assert block.signer == derive_public_key(get_node_signing_key()) block_message = block.message signed_change_request = block_message.signed_change_request assert signed_change_request == sample_signed_change_request assert signed_change_request is not sample_signed_change_request # test that a copy of it was made assert isinstance(block_message.timestamp, datetime) assert block_message.timestamp.tzinfo is None assert block_message.timestamp - datetime.utcnow() < timedelta(seconds=1) assert block_message.block_number == 0 assert block_message.block_identifier == 'next-block-identifier' updated_account_states = block_message.updated_account_states assert isinstance(updated_account_states, dict) assert len(updated_account_states) == 4 assert updated_account_states[sender].balance == 450 - 425 - 4 - 1 assert updated_account_states[sender].balance_lock assert updated_account_states['484b3176c63d5f37d808404af1a12c4b9649cd6f6769f35bdf5a816133623fbc'].balance == 425 assert updated_account_states['484b3176c63d5f37d808404af1a12c4b9649cd6f6769f35bdf5a816133623fbc' ].balance_lock is None assert updated_account_states['ad1f8845c6a1abb6011a2a434a079a087c460657aad54329a84b406dce8bf314'].balance == 4 assert updated_account_states['ad1f8845c6a1abb6011a2a434a079a087c460657aad54329a84b406dce8bf314' ].balance_lock is None assert updated_account_states['5e12967707909e62b2bb2036c209085a784fabbc3deccefee70052b6181c8ed8'].balance == 1 assert updated_account_states['5e12967707909e62b2bb2036c209085a784fabbc3deccefee70052b6181c8ed8' ].balance_lock is None
def test_block_is_appended(file_blockchain_w_memory_storage, user_account, treasury_account_signing_key): signing_key = treasury_account_signing_key blockchain = file_blockchain_w_memory_storage filename = '00000000000000000000-00000000000000000001-block-chunk.msgpack' node_signing_key = get_node_signing_key() block1 = Block.create_from_main_transaction( blockchain=blockchain, recipient=user_account, amount=10, request_signing_key=signing_key, pv_signing_key=node_signing_key, ) blockchain.add_block(block1) block2 = Block.create_from_main_transaction( blockchain=blockchain, recipient=user_account, amount=30, request_signing_key=signing_key, pv_signing_key=node_signing_key, ) blockchain.add_block(block2) assert blockchain.block_storage.files.keys() == {filename} assert blockchain.block_storage.finalized == set()
def test_can_get_nodes_blocks_node_overrides_genesis_state_node( blockchain_directory, blockchain_genesis_state, user_account_key_pair ): blockchain = FileBlockchain(base_directory=blockchain_directory) account_number = user_account_key_pair.public blockchain_state_node = baker.make( Node, network_addresses=['https://192.168.0.32:8555/'], identifier=account_number ) blockchain_genesis_state.account_states[account_number] = AccountState(node=blockchain_state_node) blockchain.add_blockchain_state(blockchain_genesis_state) blockchain.validate() request = NodeDeclarationSignedChangeRequest.create( network_addresses=['https://127.0.0.1:8555/'], fee_amount=3, signing_key=user_account_key_pair.private ) blocks_node = request.message.node assert blocks_node.identifier block = Block.create_from_signed_change_request(blockchain, request, get_node_signing_key()) blockchain.add_block(block) assert blocks_node != blockchain_state_node assert list(blockchain.yield_nodes()) == [blocks_node]
def test_can_get_primary_validator_node(forced_memory_blockchain, api_client): blockchain = forced_memory_blockchain signing_key = get_node_signing_key() pv_node_declaration_request = NodeDeclarationSignedChangeRequest.create( network_addresses=['http://my.domain.com/'], fee_amount=3, signing_key=signing_key, ) block = Block.create_from_signed_change_request( blockchain, signed_change_request=pv_node_declaration_request, pv_signing_key=signing_key, ) blockchain.add_block(block) pv_node = pv_node_declaration_request.message.node pv_schedule_request = PrimaryValidatorScheduleSignedChangeRequest.create( 0, 99, signing_key=signing_key) block = Block.create_from_signed_change_request( blockchain, signed_change_request=pv_schedule_request, pv_signing_key=signing_key, ) blockchain.add_block(block) response = api_client.get(f'{API_V1_NODES_PREFIX}/pv/') assert response.status_code == 200 data = response.json() assert data['identifier'] == pv_node.identifier assert data['fee_amount'] == 3
def test_can_serialize_deserialize(forced_mock_blockchain, sample_signed_change_request): block = Block.create_from_signed_change_request( forced_mock_blockchain, sample_signed_change_request, get_node_signing_key() ) serialized_dict = block.serialize_to_dict() deserialized_block = Block.deserialize_from_dict(serialized_dict) assert deserialized_block == block assert deserialized_block is not block
def test_can_validate_blockchain( forced_memory_blockchain: MemoryBlockchain, treasury_account_key_pair: KeyPair, user_account_key_pair: KeyPair, ): user_account = user_account_key_pair.public treasury_account = treasury_account_key_pair.public blockchain = forced_memory_blockchain blockchain.validate(is_partial_allowed=False) block0 = Block.create_from_main_transaction( blockchain=blockchain, recipient=user_account, amount=30, request_signing_key=treasury_account_key_pair.private, pv_signing_key=get_node_signing_key(), ) blockchain.add_block(block0) blockchain.snapshot_blockchain_state() blockchain.validate(is_partial_allowed=False) block1 = Block.create_from_main_transaction( blockchain=blockchain, recipient=user_account, amount=10, request_signing_key=treasury_account_key_pair.private, pv_signing_key=get_node_signing_key(), ) blockchain.add_block(block1) blockchain.snapshot_blockchain_state() blockchain.validate(is_partial_allowed=False) block2 = Block.create_from_main_transaction( blockchain=blockchain, recipient=treasury_account, amount=10, request_signing_key=user_account_key_pair.private, pv_signing_key=get_node_signing_key(), ) blockchain.add_block(block2) blockchain.snapshot_blockchain_state() blockchain.validate(is_partial_allowed=False)
def set_up(file_blockchain_w_memory_storage, user_account, treasury_account_signing_key): signing_key = treasury_account_signing_key block = Block.create_from_main_transaction( blockchain=file_blockchain_w_memory_storage, recipient=user_account, amount=100, request_signing_key=signing_key, pv_signing_key=get_node_signing_key(), ) file_blockchain_w_memory_storage.add_block(block)
def create_from_signed_change_request( cls: Type[T], blockchain, signed_change_request: SignedChangeRequest) -> T: signing_key = get_node_signing_key() block = cls(signer=derive_verify_key(signing_key), message=BlockMessage.from_signed_change_request( blockchain, signed_change_request)) block.sign(signing_key) block.hash_message() return block
def set_up(file_blockchain_w_memory_storage, user_account, treasury_account_signing_key): signing_key = treasury_account_signing_key with patch.object(file_blockchain_w_memory_storage, 'snapshot_period_in_blocks', 2): block0 = Block.create_from_main_transaction( blockchain=file_blockchain_w_memory_storage, recipient=user_account, amount=10, request_signing_key=signing_key, pv_signing_key=get_node_signing_key() ) file_blockchain_w_memory_storage.add_block(block0) block1 = Block.create_from_main_transaction( blockchain=file_blockchain_w_memory_storage, recipient=user_account, amount=20, request_signing_key=signing_key, pv_signing_key=get_node_signing_key() ) file_blockchain_w_memory_storage.add_block(block1)
def set_up(file_blockchain_w_memory_storage, user_account, treasury_account_signing_key): signing_key = treasury_account_signing_key blockchain = file_blockchain_w_memory_storage filename1 = '0000-0001-block-chunk.msgpack' block0 = Block.create_from_main_transaction( blockchain=blockchain, recipient=user_account, amount=10, request_signing_key=signing_key, pv_signing_key=get_node_signing_key(), ) blockchain.block_storage.append(filename1, block0.to_messagepack()) block1 = Block.create_from_main_transaction( blockchain=blockchain, recipient=user_account, amount=20, request_signing_key=signing_key, pv_signing_key=get_node_signing_key(), ) blockchain.block_storage.append(filename1, block1.to_messagepack()) filename2 = '0002-0003-block-chunk.msgpack' block2 = Block.create_from_main_transaction( blockchain=blockchain, recipient=user_account, amount=50, request_signing_key=signing_key, pv_signing_key=get_node_signing_key(), ) blockchain.block_storage.append(filename2, block2.to_messagepack()) block3 = Block.create_from_main_transaction( blockchain=blockchain, recipient=user_account, amount=70, request_signing_key=signing_key, pv_signing_key=get_node_signing_key(), ) blockchain.block_storage.append(filename2, block3.to_messagepack())
def add_blocks_to_blockchain(blockchain, block_count, treasury_account_private_key): treasury_account_key_pair = KeyPair( public=derive_public_key(treasury_account_private_key), private=treasury_account_private_key ) generate_blockchain( blockchain, block_count, get_node_signing_key(), add_blockchain_genesis_state=False, validate=False, treasury_account_key_pair=treasury_account_key_pair )
def test_node_identifier_is_removed_when_node_declaration_signed_change_request_is_serialized( memory_blockchain, user_account_key_pair): request = NodeDeclarationSignedChangeRequest.create( network_addresses=['127.0.0.1'], fee_amount=3, fee_account=hexstr('dcba'), signing_key=user_account_key_pair.private) block = Block.create_from_signed_change_request(memory_blockchain, request, get_node_signing_key()) compact_dict = block.to_compact_dict() assert ck('identifier') not in compact_dict[ck('message')][ck( 'signed_change_request')][ck('message')][ck('node')]
def test_can_get_nodes_single_node_from_blocks(blockchain_directory, blockchain_genesis_state, user_account_key_pair): blockchain = FileBlockchain(base_directory=blockchain_directory) blockchain.add_blockchain_state(blockchain_genesis_state) blockchain.validate() request = NodeDeclarationSignedChangeRequest.create( network_addresses=['https://127.0.0.1:8555/'], fee_amount=3, signing_key=user_account_key_pair.private ) node = request.message.node assert node.identifier block = Block.create_from_signed_change_request(blockchain, request, get_node_signing_key()) blockchain.add_block(block) assert list(blockchain.yield_nodes()) == [node]
def test_block_chunk_is_rotated(file_blockchain_w_memory_storage, user_account, treasury_account_signing_key): signing_key = treasury_account_signing_key blockchain = file_blockchain_w_memory_storage file1 = '00000000000000000000-00000000000000000001-block-chunk.msgpack' file2 = '00000000000000000002-00000000000000000003-block-chunk.msgpack' node_signing_key = get_node_signing_key() with patch.object(blockchain, 'block_chunk_size', 2): block0 = Block.create_from_main_transaction( blockchain=blockchain, recipient=user_account, amount=10, request_signing_key=signing_key, pv_signing_key=node_signing_key, ) blockchain.add_block(block0) block1 = Block.create_from_main_transaction( blockchain=blockchain, recipient=user_account, amount=10, request_signing_key=signing_key, pv_signing_key=node_signing_key, ) blockchain.add_block(block1) block2 = Block.create_from_main_transaction( blockchain=blockchain, recipient=user_account, amount=30, request_signing_key=signing_key, pv_signing_key=node_signing_key, ) blockchain.add_block(block2) block3 = Block.create_from_main_transaction( blockchain=blockchain, recipient=user_account, amount=50, request_signing_key=signing_key, pv_signing_key=node_signing_key, ) blockchain.add_block(block3) assert blockchain.block_storage.files.keys() == {file1, file2} assert blockchain.block_storage.finalized == {file1, file2}
def test_normalized_block_message(forced_mock_blockchain, sample_signed_change_request): expected_message_template = ( '{' '"block_identifier":"next-block-identifier",' '"block_number":0,' '"block_type":"ct",' '"signed_change_request":' '{"message":{"balance_lock":' '"4d3cf1d9e4547d324de2084b568f807ef12045075a7a01b8bec1e7f013fc3732",' '"txs":' '[{"amount":425,"recipient":"484b3176c63d5f37d808404af1a12c4b9649cd6f6769f35bdf5a816133623fbc"},' '{"amount":1,"is_fee":true,"recipient":"5e12967707909e62b2bb2036c209085a784fabbc3deccefee70052b6181c8ed8"},' '{"amount":4,"is_fee":true,"recipient":' '"ad1f8845c6a1abb6011a2a434a079a087c460657aad54329a84b406dce8bf314"}]},' '"signature":"362dc47191d5d1a33308de1f036a5e93fbaf0b05fa971d9537f954f13cd22b5ed9bee56f4701bd' 'af9b995c47271806ba73e75d63f46084f5830cec5f5b7e9600",' '"signer":"4d3cf1d9e4547d324de2084b568f807ef12045075a7a01b8bec1e7f013fc3732"},' '"timestamp":"<replace-with-timestamp>",' '"updated_account_states":{' '"484b3176c63d5f37d808404af1a12c4b9649cd6f6769f35bdf5a816133623fbc":{"balance":425},' '"4d3cf1d9e4547d324de2084b568f807ef12045075a7a01b8bec1e7f013fc3732":' '{' '"balance":20,' '"balance_lock":"ff3127bdb408e5f3f4f07dd364ce719b2854dc28ee66aa7af839e46468761885"' '},' '"5e12967707909e62b2bb2036c209085a784fabbc3deccefee70052b6181c8ed8":{"balance":1},' '"ad1f8845c6a1abb6011a2a434a079a087c460657aad54329a84b406dce8bf314":{"balance":4}' '}' '}' ) def get_account_balance(self, account, on_block_number): return 450 if account == sample_signed_change_request.signer else 0 with patch.object(MockBlockchain, 'get_account_balance', new=get_account_balance): block = Block.create_from_signed_change_request( forced_mock_blockchain, sample_signed_change_request, get_node_signing_key() ) expected_message = expected_message_template.replace( '<replace-with-timestamp>', block.message.timestamp.isoformat() ).encode('utf-8') assert block.message.get_normalized() == expected_message
def test_cannot_add_block_twice(file_blockchain_w_memory_storage, user_account, treasury_account_signing_key): signing_key = treasury_account_signing_key blockchain = file_blockchain_w_memory_storage node_signing_key = get_node_signing_key() block = Block.create_from_main_transaction( blockchain=blockchain, recipient=user_account, amount=10, request_signing_key=signing_key, pv_signing_key=node_signing_key, ) blockchain.add_block(block) with pytest.raises( ValidationError, match='Block number must be equal to next block number.*'): blockchain.add_block(block)
def test_create_node_declaration_block(memory_blockchain, user_account_key_pair): request = NodeDeclarationSignedChangeRequest.create( network_addresses=['127.0.0.1'], fee_amount=3, fee_account=hexstr('dcba'), signing_key=user_account_key_pair.private) block = Block.create_from_signed_change_request(memory_blockchain, request, get_node_signing_key()) assert block assert block.message.signed_change_request assert block.message.signed_change_request.signer assert block.message.signed_change_request.signature assert block.message.signed_change_request.message assert block.message.signed_change_request.message.node.network_addresses == [ '127.0.0.1' ] assert block.message.signed_change_request.message.node.fee_amount == 3 assert block.message.signed_change_request.message.node.fee_account == hexstr( 'dcba')
def test_node_serializes_without_identifier_in_block(memory_blockchain, user_account_key_pair): account_number = user_account_key_pair.public node = baker.make(Node, identifier=account_number) request = NodeDeclarationSignedChangeRequest.create( network_addresses=['https://192.168.0.34:8555/'], fee_amount=3, signing_key=user_account_key_pair.private) block = Block.create_from_signed_change_request(memory_blockchain, request, get_node_signing_key()) serialized = block.serialize_to_dict() assert 'identifier' not in serialized['message']['updated_account_states'][ account_number]['node'] deserialized = Block.deserialize_from_dict(serialized) deserialized_node = deserialized.message.updated_account_states[ account_number].node assert deserialized_node is not node assert deserialized_node.identifier == account_number
def set_up( file_blockchain_w_memory_storage, user_account, treasury_account_signing_key, primary_validator_identifier, node_identifier ): signing_key = treasury_account_signing_key pv = PrimaryValidator( identifier=primary_validator_identifier, fee_amount=primary_validator_fee, network_addresses=[] ) node = RegularNode(identifier=node_identifier, fee_amount=node_fee, network_addresses=[]) block = Block.create_from_main_transaction( blockchain=file_blockchain_w_memory_storage, recipient=user_account, amount=100, request_signing_key=signing_key, pv_signing_key=get_node_signing_key(), primary_validator=pv, node=node ) file_blockchain_w_memory_storage.add_block(block) file_blockchain_w_memory_storage.snapshot_blockchain_state()
def test_can_get_self_node(forced_memory_blockchain, api_client): blockchain = forced_memory_blockchain signing_key = get_node_signing_key() node_id = derive_public_key(signing_key) node_declaration_request = NodeDeclarationSignedChangeRequest.create( network_addresses=['http://my.domain.com/'], fee_amount=3, signing_key=signing_key, ) block = Block.create_from_signed_change_request( blockchain, signed_change_request=node_declaration_request, pv_signing_key=signing_key, ) blockchain.add_block(block) response = api_client.get(f'{API_V1_NODES_PREFIX}/self/') assert response.status_code == 200 data = response.json() assert data['identifier'] == node_id assert data['fee_amount'] == 3
def test_can_get_nodes_from_different_block_numbers( blockchain_directory, blockchain_genesis_state, user_account_key_pair ): account_number = user_account_key_pair.public blockchain = FileBlockchain(base_directory=blockchain_directory) blockchain_state_node = baker.make( Node, network_addresses=['https://192.168.0.29:8555/'], identifier=account_number ) blockchain_genesis_state.account_states[account_number] = AccountState(node=blockchain_state_node) blockchain.add_blockchain_state(blockchain_genesis_state) blockchain.validate() request = NodeDeclarationSignedChangeRequest.create( network_addresses=['https://192.168.0.30:8555/'], fee_amount=3, signing_key=user_account_key_pair.private ) node0 = request.message.node blockchain.add_block(Block.create_from_signed_change_request(blockchain, request, get_node_signing_key())) request = NodeDeclarationSignedChangeRequest.create( network_addresses=['https://192.168.0.31:8555/'], fee_amount=3, signing_key=user_account_key_pair.private ) node1 = request.message.node blockchain.add_block(Block.create_from_signed_change_request(blockchain, request, get_node_signing_key())) request = NodeDeclarationSignedChangeRequest.create( network_addresses=['https://192.168.0.32:8555/'], fee_amount=3, signing_key=user_account_key_pair.private ) node2 = request.message.node blockchain.add_block(Block.create_from_signed_change_request(blockchain, request, get_node_signing_key())) assert list(blockchain.yield_nodes()) == [node2] assert list(blockchain.yield_nodes(block_number=2)) == [node2] assert list(blockchain.yield_nodes(block_number=1)) == [node1] assert list(blockchain.yield_nodes(block_number=0)) == [node0] assert list(blockchain.yield_nodes(block_number=-1)) == [blockchain_state_node]
def test_can_add_block( blockchain_directory, blockchain_genesis_state, treasury_account_key_pair: KeyPair, user_account_key_pair: KeyPair, primary_validator_key_pair: KeyPair, node_key_pair: KeyPair, ): blockchain = FileBlockchain(base_directory=blockchain_directory) blockchain.add_blockchain_state(blockchain_genesis_state) blockchain.validate() treasury_account = treasury_account_key_pair.public treasury_initial_balance = blockchain.get_account_current_balance( treasury_account) assert treasury_initial_balance is not None user_account = user_account_key_pair.public pv_account = primary_validator_key_pair.public node_account = node_key_pair.public total_fees = 1 + 4 node_signing_key = get_node_signing_key() block0 = Block.create_from_main_transaction( blockchain=blockchain, recipient=user_account, amount=30, request_signing_key=treasury_account_key_pair.private, pv_signing_key=node_signing_key, ) blockchain.add_block(block0) assert blockchain.get_account_current_balance(user_account) == 30 assert blockchain.get_account_current_balance( treasury_account) == treasury_initial_balance - 30 - total_fees assert blockchain.get_account_current_balance(node_account) == 1 assert blockchain.get_account_current_balance(pv_account) == 4 with pytest.raises( ValidationError, match='Block number must be equal to next block number.*'): blockchain.add_block(block0) block1 = Block.create_from_main_transaction( blockchain=blockchain, recipient=user_account, amount=10, request_signing_key=treasury_account_key_pair.private, pv_signing_key=node_signing_key, ) blockchain.add_block(block1) assert blockchain.get_account_current_balance(user_account) == 40 assert blockchain.get_account_current_balance(treasury_account) == ( treasury_initial_balance - 30 - 10 - 2 * total_fees) assert blockchain.get_account_current_balance(node_account) == 1 * 2 assert blockchain.get_account_current_balance(pv_account) == 4 * 2 block2 = Block.create_from_main_transaction( blockchain=blockchain, recipient=treasury_account, amount=5, request_signing_key=user_account_key_pair.private, pv_signing_key=node_signing_key, ) blockchain.add_block(block2) assert blockchain.get_account_current_balance( user_account) == 40 - 5 - total_fees assert blockchain.get_account_current_balance(treasury_account) == ( treasury_initial_balance - 30 - 10 + 5 - 2 * total_fees) assert blockchain.get_account_current_balance(node_account) == 1 * 3 assert blockchain.get_account_current_balance(pv_account) == 4 * 3
def test_partial_blockchain(primary_validator, preferred_node): account1_key_pair = generate_key_pair() account2_key_pair = generate_key_pair() account3_key_pair = generate_key_pair() new_account_key_pair = generate_key_pair() fake_lock1, _ = generate_key_pair() fake_lock2, _ = generate_key_pair() fake_lock3, _ = generate_key_pair() base_account_root_file = BlockchainState( account_states={ account1_key_pair.public: AccountState(balance=1000, balance_lock=fake_lock1), account2_key_pair.public: AccountState(balance=2000, balance_lock=fake_lock2), account3_key_pair.public: AccountState(balance=3000, balance_lock=fake_lock3), }, last_block_number=1234, last_block_identifier='23203d245b5e128465669223b5220b3061af1e2e72b0429ef26b07ce3a2282e7', last_block_timestamp=datetime.utcnow(), next_block_identifier='626dea61c1a6480d6a4c9cd657c7d7be52ddc38e5f2ec590b609ac01edde62fd', ) blockchain = MemoryBlockchain(account_root_files=[base_account_root_file]) assert blockchain.get_block_count() == 0 assert blockchain.get_account_current_balance(account1_key_pair.public) == 1000 assert blockchain.get_account_current_balance(account2_key_pair.public) == 2000 assert blockchain.get_account_current_balance(account3_key_pair.public) == 3000 assert blockchain.get_account_current_balance(new_account_key_pair.public) == 0 blockchain.validate() signed_change_request1 = CoinTransferSignedChangeRequest.from_main_transaction( blockchain=blockchain, recipient=account2_key_pair.public, amount=10, signing_key=account1_key_pair.private, primary_validator=primary_validator, node=preferred_node ) signed_change_request1.validate(blockchain, blockchain.get_next_block_number()) blockchain.add_block_from_signed_change_request(signed_change_request1, get_node_signing_key()) blockchain.validate() assert blockchain.get_block_count() == 1 assert blockchain.get_account_current_balance(account1_key_pair.public) == 1000 - 10 - 4 - 1 assert blockchain.get_account_current_balance(account2_key_pair.public) == 2000 + 10 assert blockchain.get_account_current_balance(account3_key_pair.public) == 3000 assert blockchain.get_account_current_balance(new_account_key_pair.public) == 0 signed_change_request2 = CoinTransferSignedChangeRequest.from_main_transaction( blockchain=blockchain, recipient=new_account_key_pair.public, amount=20, signing_key=account2_key_pair.private, primary_validator=primary_validator, node=preferred_node ) signed_change_request2.validate(blockchain, blockchain.get_next_block_number()) blockchain.add_block_from_signed_change_request(signed_change_request2, get_node_signing_key()) blockchain.validate() assert blockchain.get_block_count() == 2 assert blockchain.get_account_current_balance(account1_key_pair.public) == 1000 - 10 - 4 - 1 assert blockchain.get_account_current_balance(account2_key_pair.public) == 2000 + 10 - 20 - 4 - 1 assert blockchain.get_account_current_balance(account3_key_pair.public) == 3000 assert blockchain.get_account_current_balance(new_account_key_pair.public) == 20 blockchain.snapshot_blockchain_state() blockchain.validate() assert blockchain.get_account_current_balance(account1_key_pair.public) == 1000 - 10 - 4 - 1 assert blockchain.get_account_current_balance(account2_key_pair.public) == 2000 + 10 - 20 - 4 - 1 assert blockchain.get_account_current_balance(account3_key_pair.public) == 3000 assert blockchain.get_account_current_balance(new_account_key_pair.public) == 20 signed_change_request3 = CoinTransferSignedChangeRequest.from_main_transaction( blockchain=blockchain, recipient=account2_key_pair.public, amount=30, signing_key=account3_key_pair.private, primary_validator=primary_validator, node=preferred_node ) signed_change_request3.validate(blockchain, blockchain.get_next_block_number()) blockchain.add_block_from_signed_change_request(signed_change_request3, get_node_signing_key()) blockchain.validate() assert blockchain.get_account_current_balance(account1_key_pair.public) == 1000 - 10 - 4 - 1 assert blockchain.get_account_current_balance(account2_key_pair.public) == 2000 + 10 - 20 - 4 - 1 + 30 assert blockchain.get_account_current_balance(account3_key_pair.public) == 3000 - 30 - 4 - 1 assert blockchain.get_account_current_balance(new_account_key_pair.public) == 20
def test_can_get_nodes_from_complex_blockchain(blockchain_directory, blockchain_genesis_state): key_pair1 = generate_key_pair() key_pair2 = generate_key_pair() key_pair3 = generate_key_pair() key_pair4 = generate_key_pair() key_pair5 = generate_key_pair() key_pair6 = generate_key_pair() assert len({ key_pair1.public, key_pair2.public, key_pair3.public, key_pair4.public, key_pair5.public, key_pair6.public }) == 6 blockchain = FileBlockchain(base_directory=blockchain_directory) node1 = baker.make(Node, network_addresses=['https://192.168.0.29:8555/'], identifier=key_pair1.public) blockchain_genesis_state.account_states[node1.identifier] = AccountState(node=node1) blockchain.add_blockchain_state(blockchain_genesis_state) blockchain.validate() # Block 0 request = NodeDeclarationSignedChangeRequest.create( network_addresses=['https://192.168.0.30:8555/'], fee_amount=3, signing_key=key_pair2.private ) node2 = request.message.node blockchain.add_block(Block.create_from_signed_change_request(blockchain, request, get_node_signing_key())) # Block 1 request = NodeDeclarationSignedChangeRequest.create( network_addresses=['https://192.168.0.31:8555/'], fee_amount=3, signing_key=key_pair3.private ) node3_old = request.message.node blockchain.add_block(Block.create_from_signed_change_request(blockchain, request, get_node_signing_key())) # Block 2 request = NodeDeclarationSignedChangeRequest.create( network_addresses=['https://192.168.0.32:8555/'], fee_amount=3, signing_key=key_pair4.private ) node4 = request.message.node blockchain.add_block(Block.create_from_signed_change_request(blockchain, request, get_node_signing_key())) def sort_key(value): return value.network_addresses def sort_me(list_): return sorted(list_, key=sort_key) assert sort_me(blockchain.yield_nodes(block_number=2)) == sort_me([node4, node3_old, node2, node1]) blockchain.snapshot_blockchain_state() # Block 3 request = NodeDeclarationSignedChangeRequest.create( network_addresses=['https://192.168.0.33:8555/'], fee_amount=3, signing_key=key_pair3.private ) node3 = request.message.node blockchain.add_block(Block.create_from_signed_change_request(blockchain, request, get_node_signing_key())) # Block 4 request = NodeDeclarationSignedChangeRequest.create( network_addresses=['https://192.168.0.34:8555/'], fee_amount=3, signing_key=key_pair5.private ) node5 = request.message.node blockchain.add_block(Block.create_from_signed_change_request(blockchain, request, get_node_signing_key())) # Block 5 request = NodeDeclarationSignedChangeRequest.create( network_addresses=['https://192.168.0.35:8555/'], fee_amount=3, signing_key=key_pair6.private ) node6 = request.message.node blockchain.add_block(Block.create_from_signed_change_request(blockchain, request, get_node_signing_key())) assert sort_me(blockchain.yield_nodes()) == sort_me([node6, node5, node3, node4, node2, node1]) assert sort_me(blockchain.yield_nodes(block_number=5)) == sort_me([node6, node5, node3, node4, node2, node1]) assert sort_me(blockchain.yield_nodes(block_number=4)) == sort_me([node5, node3, node4, node2, node1]) assert sort_me(blockchain.yield_nodes(block_number=3)) == sort_me([node3, node4, node2, node1]) assert sort_me(blockchain.yield_nodes(block_number=2)) == sort_me([node4, node3_old, node2, node1]) assert sort_me(blockchain.yield_nodes(block_number=1)) == sort_me([node3_old, node2, node1]) assert sort_me(blockchain.yield_nodes(block_number=0)) == sort_me([node2, node1]) assert sort_me(blockchain.yield_nodes(block_number=-1)) == sort_me([node1])
def test_can_create_block_from_main_transaction( forced_mock_blockchain, treasury_account_key_pair: KeyPair, user_account_key_pair: KeyPair, primary_validator_key_pair: KeyPair, node_key_pair: KeyPair ): def get_account_balance(self, account, on_block_number): return 430 if account == treasury_account_key_pair.public else 0 with patch.object(MockBlockchain, 'get_account_balance', new=get_account_balance): block = Block.create_from_main_transaction( forced_mock_blockchain, user_account_key_pair.public, 20, signing_key=treasury_account_key_pair.private ) # Assert block assert block.message assert block.hash assert block.signature block.validate_signature() assert block.signer assert block.signer == derive_verify_key(get_node_signing_key()) # Assert block.message block_message = block.message assert block_message assert isinstance(block_message.timestamp, datetime) assert block_message.timestamp.tzinfo is None assert block_message.timestamp - datetime.utcnow() < timedelta(seconds=1) assert block_message.block_number == 0 assert block_message.block_identifier == 'next-block-identifier' updated_account_states = block_message.updated_account_states assert isinstance(updated_account_states, dict) assert len(updated_account_states) == 4 assert updated_account_states[treasury_account_key_pair.public].balance == 430 - 25 assert updated_account_states[treasury_account_key_pair.public].balance_lock assert updated_account_states[user_account_key_pair.public].balance == 20 assert updated_account_states[user_account_key_pair.public].balance_lock is None assert updated_account_states[primary_validator_key_pair.public].balance == 4 assert updated_account_states[primary_validator_key_pair.public].balance_lock is None assert updated_account_states[node_key_pair.public].balance == 1 assert updated_account_states[node_key_pair.public].balance_lock is None # Assert block_message.signed_change_request signed_change_request = block_message.signed_change_request assert signed_change_request.signer == treasury_account_key_pair.public assert signed_change_request.signature # Assert block_message.signed_change_request.message coin_transfer_signed_request_message = signed_change_request.message assert isinstance(coin_transfer_signed_request_message, CoinTransferSignedChangeRequestMessage) assert coin_transfer_signed_request_message.balance_lock assert len(coin_transfer_signed_request_message.txs) == 3 txs_dict = {tx.recipient: tx for tx in coin_transfer_signed_request_message.txs} assert len(txs_dict) == 3 assert txs_dict[user_account_key_pair.public].amount == 20 assert txs_dict[user_account_key_pair.public].is_fee is False assert txs_dict[primary_validator_key_pair.public].amount == 4 assert txs_dict[primary_validator_key_pair.public].is_fee assert txs_dict[node_key_pair.public].amount == 1 assert txs_dict[node_key_pair.public].is_fee assert coin_transfer_signed_request_message.get_total_amount() == 25
def test_can_get_account_state_by_block_number( forced_memory_blockchain: MemoryBlockchain, treasury_account_key_pair: KeyPair, user_account_key_pair: KeyPair, primary_validator, preferred_node): blockchain = forced_memory_blockchain sender = treasury_account_key_pair.public recipient = user_account_key_pair.public total_fees = primary_validator.fee_amount + preferred_node.fee_amount sender_initial_balance = blockchain.get_account_current_balance(sender) assert sender_initial_balance == 281474976710656 assert blockchain.get_account_balance(sender, -1) == sender_initial_balance assert blockchain.get_account_balance(recipient, -1) == 0 assert blockchain.get_account_current_balance(recipient) == 0 block0 = Block.create_from_main_transaction( blockchain=blockchain, recipient=recipient, amount=10, request_signing_key=treasury_account_key_pair.private, pv_signing_key=get_node_signing_key(), ) blockchain.add_block(block0) assert blockchain.get_account_balance(sender, -1) == sender_initial_balance assert blockchain.get_account_balance(recipient, -1) == 0 assert blockchain.get_account_balance( sender, 0) == sender_initial_balance - 10 - total_fees assert blockchain.get_account_balance(recipient, 0) == 10 assert blockchain.get_account_current_balance( sender) == sender_initial_balance - 10 - total_fees assert blockchain.get_account_current_balance(recipient) == 10 block1 = Block.create_from_main_transaction( blockchain=blockchain, recipient=recipient, amount=11, request_signing_key=treasury_account_key_pair.private, pv_signing_key=get_node_signing_key(), ) blockchain.add_block(block1) assert blockchain.get_account_balance(sender, -1) == sender_initial_balance assert blockchain.get_account_balance(recipient, -1) == 0 assert blockchain.get_account_balance( sender, 0) == sender_initial_balance - 10 - total_fees assert blockchain.get_account_balance(recipient, 0) == 10 assert blockchain.get_account_balance( sender, 1) == sender_initial_balance - 10 - 11 - 2 * total_fees assert blockchain.get_account_balance(recipient, 1) == 10 + 11 assert blockchain.get_account_current_balance( sender) == sender_initial_balance - 10 - 11 - 2 * total_fees assert blockchain.get_account_current_balance(recipient) == 10 + 11 block2 = Block.create_from_main_transaction( blockchain=blockchain, recipient=recipient, amount=12, request_signing_key=treasury_account_key_pair.private, pv_signing_key=get_node_signing_key(), ) blockchain.add_block(block2) assert blockchain.get_account_balance(sender, -1) == sender_initial_balance assert blockchain.get_account_balance(recipient, -1) == 0 assert blockchain.get_account_balance( sender, 0) == sender_initial_balance - 10 - total_fees assert blockchain.get_account_balance(recipient, 0) == 10 assert blockchain.get_account_balance( sender, 1) == sender_initial_balance - 10 - 11 - 2 * total_fees assert blockchain.get_account_balance(recipient, 1) == 10 + 11 assert blockchain.get_account_balance( sender, 2) == sender_initial_balance - 10 - 11 - 12 - 3 * total_fees assert blockchain.get_account_balance(recipient, 2) == 10 + 11 + 12 assert blockchain.get_account_current_balance( sender) == sender_initial_balance - 10 - 11 - 12 - 3 * total_fees assert blockchain.get_account_current_balance(recipient) == 10 + 11 + 12
def test_can_make_blockchain_state_on_last_block( forced_memory_blockchain: BlockchainBase, blockchain_genesis_state, treasury_account_key_pair, user_account_key_pair, primary_validator, preferred_node): blockchain = forced_memory_blockchain user_account = user_account_key_pair.public treasury_account = treasury_account_key_pair.public treasury_initial_balance = blockchain.get_account_current_balance( treasury_account) assert treasury_initial_balance is not None assert blockchain.get_last_blockchain_state() == blockchain_genesis_state assert blockchain.get_blockchain_state_by_block_number( -1) == blockchain_genesis_state assert blockchain_genesis_state.account_states[ treasury_account].balance_lock == treasury_account assert blockchain.get_blockchain_states_count() == 1 blockchain.snapshot_blockchain_state() assert blockchain.get_blockchain_states_count() == 1 block0 = Block.create_from_main_transaction( blockchain=blockchain, recipient=user_account, amount=30, request_signing_key=treasury_account_key_pair.private, pv_signing_key=get_node_signing_key(), ) blockchain.add_block(block0) blockchain.snapshot_blockchain_state() assert blockchain.get_blockchain_states_count() == 2 blockchain.snapshot_blockchain_state() assert blockchain.get_blockchain_states_count() == 2 account_root_file = blockchain.get_last_blockchain_state() assert account_root_file is not None assert account_root_file.last_block_number == 0 assert account_root_file.last_block_identifier == block0.message.block_identifier assert account_root_file.next_block_identifier == block0.hash assert len(account_root_file.account_states) == 4 assert account_root_file.account_states.keys() == { user_account, treasury_account, primary_validator.identifier, preferred_node.identifier } assert account_root_file.account_states[user_account].balance == 30 assert account_root_file.account_states[user_account].balance_lock is None assert account_root_file.account_states[user_account].get_balance_lock( user_account) == user_account assert account_root_file.account_states[ treasury_account].balance == treasury_initial_balance - 30 - 4 - 1 assert account_root_file.account_states[ treasury_account].balance_lock != treasury_account assert account_root_file.account_states[ primary_validator.identifier].balance == 4 assert account_root_file.account_states[ primary_validator.identifier].balance_lock is None assert account_root_file.account_states[ primary_validator.identifier].get_balance_lock( primary_validator.identifier) == primary_validator.identifier assert account_root_file.account_states[ preferred_node.identifier].balance == 1 assert account_root_file.account_states[ preferred_node.identifier].balance_lock is None assert account_root_file.account_states[ preferred_node.identifier].get_balance_lock( preferred_node.identifier) == preferred_node.identifier block1 = Block.create_from_main_transaction( blockchain=blockchain, recipient=treasury_account, amount=20, request_signing_key=user_account_key_pair.private, pv_signing_key=get_node_signing_key(), ) blockchain.add_block(block1) block2 = Block.create_from_main_transaction( blockchain=blockchain, recipient=primary_validator.identifier, amount=2, request_signing_key=treasury_account_key_pair.private, pv_signing_key=get_node_signing_key(), ) blockchain.add_block(block2) blockchain.snapshot_blockchain_state() account_root_file = blockchain.get_last_blockchain_state() assert account_root_file is not None assert account_root_file.last_block_number == 2 assert account_root_file.last_block_identifier == block2.message.block_identifier assert account_root_file.next_block_identifier == block2.hash assert len(account_root_file.account_states) == 4 assert account_root_file.account_states.keys() == { user_account, treasury_account, primary_validator.identifier, preferred_node.identifier } assert account_root_file.account_states[user_account].balance == 5 assert account_root_file.account_states[ user_account].balance_lock != user_account assert account_root_file.account_states[ treasury_account].balance == treasury_initial_balance - 30 - 4 - 1 + 20 - 2 - 4 - 1 assert account_root_file.account_states[ treasury_account].balance_lock != treasury_account assert account_root_file.account_states[ primary_validator.identifier].balance == 4 + 4 + 4 + 2 assert account_root_file.account_states[ primary_validator.identifier].balance_lock is None assert account_root_file.account_states[ primary_validator.identifier].get_balance_lock( primary_validator.identifier) == primary_validator.identifier assert account_root_file.account_states[ preferred_node.identifier].balance == 1 + 1 + 1 assert account_root_file.account_states[ preferred_node.identifier].balance_lock is None assert account_root_file.account_states[ preferred_node.identifier].get_balance_lock( preferred_node.identifier) == preferred_node.identifier
def test_can_duplicate_recipients( forced_mock_blockchain: MockBlockchain, treasury_account_key_pair: KeyPair, user_account_key_pair: KeyPair ): def get_account_balance(self, account, on_block_number): return 430 if account == treasury_account_key_pair.public else 10 sender = treasury_account_key_pair.public recipient = user_account_key_pair.public message = CoinTransferSignedChangeRequestMessage( balance_lock=forced_mock_blockchain.get_account_current_balance_lock(sender), txs=[ CoinTransferTransaction(recipient=recipient, amount=3), CoinTransferTransaction(recipient=recipient, amount=5), ] ) request = CoinTransferSignedChangeRequest.create_from_signed_change_request_message( message, treasury_account_key_pair.private ) with patch.object(MockBlockchain, 'get_account_balance', new=get_account_balance): block = Block.create_from_signed_change_request(forced_mock_blockchain, request, get_node_signing_key()) updated_account_states = block.message.updated_account_states assert len(updated_account_states) == 2 sender_account_state = block.message.get_account_state(treasury_account_key_pair.public) assert sender_account_state assert sender_account_state.balance == 430 - 3 - 5 assert sender_account_state.balance_lock recipient_account_state = block.message.get_account_state(user_account_key_pair.public) assert recipient_account_state assert recipient_account_state.balance == 10 + 3 + 5