def test_public_key_handler_revoke(): """ Case: send transaction request to revoke certificate public key. Expect: public key storage blockchain record is changed to True. """ revoke_public_key_payload = RevokePubKeyPayload( address=ADDRESS_FROM_CERTIFICATE_PUBLIC_KEY, ) transaction_payload = TransactionPayload() transaction_payload.method = PubKeyMethod.REVOKE transaction_payload.data = revoke_public_key_payload.SerializeToString() serialized_transaction_payload = transaction_payload.SerializeToString() transaction_header = generate_header(serialized_transaction_payload, INPUTS, OUTPUTS) serialized_header = transaction_header.SerializeToString() transaction_request = TpProcessRequest( header=transaction_header, payload=serialized_transaction_payload, signature=create_signer(private_key=SENDER_PRIVATE_KEY).sign(serialized_header), ) existing_public_key_payload = generate_rsa_payload() existing_public_key_storage = PubKeyStorage() existing_public_key_storage.owner = SENDER_PUBLIC_KEY existing_public_key_storage.payload.CopyFrom(existing_public_key_payload) existing_public_key_storage.is_revoked = False serialized_existing_public_key_storage = existing_public_key_storage.SerializeToString() mock_context = StubContext(inputs=INPUTS, outputs=OUTPUTS, initial_state={ ADDRESS_FROM_CERTIFICATE_PUBLIC_KEY: serialized_existing_public_key_storage, }) expected_public_key_payload = generate_rsa_payload() expected_public_key_storage = PubKeyStorage() expected_public_key_storage.owner = SENDER_PUBLIC_KEY expected_public_key_storage.payload.CopyFrom(expected_public_key_payload) expected_public_key_storage.is_revoked = True serialized_expected_public_key_storage = expected_public_key_storage.SerializeToString() expected_state = { ADDRESS_FROM_CERTIFICATE_PUBLIC_KEY: serialized_expected_public_key_storage, } PubKeyHandler().apply(transaction=transaction_request, context=mock_context) state_as_list = mock_context.get_state(addresses=[ADDRESS_FROM_CERTIFICATE_PUBLIC_KEY]) state_as_dict = {entry.address: entry.data for entry in state_as_list} assert expected_state == state_as_dict
def test_store_success(self): context = self.get_context() cert, key, _ = create_certificate(context.pub_key_payload, signer=context.client.get_signer()) cert_address, transaction_payload = self._pre_parse_payload_and_exec( context, cert, key) crt_export, crt_bin, crt_sig, rem_sig, pub_key, \ valid_from, valid_to = get_crt_export_bin_sig_rem_sig(cert, key, context.client) account = AccountClient.get_account_model(PUB_KEY_STORE_PRICE) data = PubKeyStorage() data.owner = self.account_signer1.get_public_key().as_hex() data.payload.CopyFrom(transaction_payload) data.revoked = False self.expect_get({cert_address: None, self.account_address1: account}) self.expect_get({_make_settings_key('remme.economy_enabled'): None}) context.client.store_pub_key(pub_key, rem_sig, crt_sig, valid_from, valid_to) account.balance -= PUB_KEY_STORE_PRICE account.pub_keys.append(cert_address) self.expect_set({self.account_address1: account, cert_address: data}) self.expect_ok()
def _store_pub_key(self, context, signer_pubkey, transaction_payload): address = self.make_address_from_data(transaction_payload.public_key) LOGGER.info('Pub key address {}'.format(address)) account_address = AccountHandler().make_address_from_data(signer_pubkey) LOGGER.info('Account address {}'.format(address)) data, account = get_multiple_data(context, [(address, PubKeyStorage), (account_address, Account)]) if data: raise InvalidTransaction('This pub key is already registered.') cert_signer_pubkey = load_pem_public_key(transaction_payload.public_key.encode('utf-8'), backend=default_backend()) try: ehs_bytes = binascii.unhexlify(transaction_payload.entity_hash_signature) eh_bytes = binascii.unhexlify(transaction_payload.entity_hash) except binascii.Error: LOGGER.debug(f'entity_hash_signature {transaction_payload.entity_hash_signature}') LOGGER.debug(f'entity_hash {transaction_payload.entity_hash}') raise InvalidTransaction('Entity hash or signature not a hex format') # FIXME: For support PKCS1v15 and PSS LOGGER.warn('HAZARD: Detecting padding for verification') sigerr = 0 pkcs = padding.PKCS1v15() pss = padding.PSS(mgf=padding.MGF1(hashes.SHA512()), salt_length=padding.PSS.MAX_LENGTH) for _padding in (pkcs, pss): try: cert_signer_pubkey.verify(ehs_bytes, eh_bytes, _padding, hashes.SHA512()) LOGGER.warn('HAZARD: Padding found: %s', _padding.name) except InvalidSignature: sigerr += 1 if sigerr == 2: raise InvalidTransaction('Invalid signature') valid_from = datetime.fromtimestamp(transaction_payload.valid_from) valid_to = datetime.fromtimestamp(transaction_payload.valid_to) if valid_to - valid_from > PUB_KEY_MAX_VALIDITY: raise InvalidTransaction('The public key validity exceeds the maximum value.') data = PubKeyStorage() data.owner = signer_pubkey data.payload.CopyFrom(transaction_payload) data.revoked = False if not account: account = Account() if _get_setting_value(context, 'remme.economy_enabled', 'true').lower() == 'true': if account.balance < PUB_KEY_STORE_PRICE: raise InvalidTransaction('Not enough tokens to register a new pub key. Current balance: {}' .format(account.balance)) account.balance -= PUB_KEY_STORE_PRICE if address not in account.pub_keys: account.pub_keys.append(address) return {address: data, account_address: account}
def test_store_success(self): context = self.get_context() cert, key, _ = PubKeyClient.create_certificate( context.pub_key_payload, signer=context.client.get_signer()) transaction_signature, cert_address, transaction_payload = self._pre_parse_payload_and_exec( context, cert, key) crt_export, crt_bin, crt_sig, rem_sig, pub_key, \ valid_from, valid_to = PubKeyClient.get_crt_export_bin_sig_rem_sig(cert, key, context.client) account = AccountClient.get_account_model(PUB_KEY_STORE_PRICE) storage_account = AccountClient.get_account_model(0) storage_signer = self.get_new_signer() storage_pub_key = storage_signer.get_public_key().as_hex() storage_address = AccountHandler().make_address_from_data( storage_pub_key) data = PubKeyStorage() data.owner = self.account_signer1.get_public_key().as_hex() data.payload.CopyFrom(transaction_payload) data.revoked = False self.expect_get({cert_address: None, self.account_address1: account}) self.expect_get({_make_settings_key('remme.economy_enabled'): None}) self.expect_get({ _make_settings_key(SETTINGS_STORAGE_PUB_KEY): get_setting_from_key_value(SETTINGS_STORAGE_PUB_KEY, storage_pub_key) }) self.expect_get({ self.account_address1: account, storage_address: storage_account }) context.client.store_pub_key(pub_key, rem_sig, crt_sig, valid_from, valid_to) account.balance -= PUB_KEY_STORE_PRICE account.pub_keys.append(cert_address) storage_account.balance += PUB_KEY_STORE_PRICE self.expect_set( transaction_signature, PubKeyMethod.STORE, { self.account_address1: account, cert_address: data, storage_address: storage_account }) self.expect_ok()
def test_store_public_key_for_other_already_registered_public_key(): """ Case: send transaction request to store already registered certificate public key for other. Expect: invalid transaction error is raised with public key is already registered error message. """ new_public_key_payload = generate_rsa_payload(key=CERTIFICATE_PUBLIC_KEY) serialized_new_public_key_payload = new_public_key_payload.SerializeToString() private_key = Secp256k1PrivateKey.from_hex(OWNER_PRIVATE_KEY) signature_by_owner = Secp256k1Context().sign(serialized_new_public_key_payload, private_key) new_public_key_store_and_pay_payload = NewPubKeyStoreAndPayPayload( pub_key_payload=new_public_key_payload, owner_public_key=bytes.fromhex(OWNER_PUBLIC_KEY), signature_by_owner=bytes.fromhex(signature_by_owner), ) transaction_payload = TransactionPayload() transaction_payload.method = PubKeyMethod.STORE_AND_PAY transaction_payload.data = new_public_key_store_and_pay_payload.SerializeToString() serialized_transaction_payload = transaction_payload.SerializeToString() transaction_header = generate_header( serialized_transaction_payload, INPUTS, OUTPUTS, signer_public_key=PAYER_PUBLIC_KEY, ) serialized_header = transaction_header.SerializeToString() transaction_request = TpProcessRequest( header=transaction_header, payload=serialized_transaction_payload, signature=create_signer(private_key=PAYER_PRIVATE_KEY).sign(serialized_header), ) already_registered_public_key = PubKeyStorage() already_registered_public_key.owner = OWNER_PUBLIC_KEY already_registered_public_key.payload.CopyFrom(new_public_key_payload) already_registered_public_key.is_revoked = False serialized_already_registered_public_key = already_registered_public_key.SerializeToString() mock_context = StubContext(inputs=INPUTS, outputs=OUTPUTS, initial_state={ ADDRESS_FROM_CERTIFICATE_PUBLIC_KEY: serialized_already_registered_public_key, }) with pytest.raises(InvalidTransaction) as error: PubKeyHandler().apply(transaction=transaction_request, context=mock_context) assert 'This public key is already registered.' == str(error.value)
def test_revoke_fail_wrong_signer(self): context = self.get_context() cert, key, key_export = create_certificate(context.pub_key_payload, org_name='different', signer=self.account_signer2) cert_address, transaction_payload = self._pre_parse_payload_and_exec(context, cert, key, 'revoke') data = PubKeyStorage() data.owner = self.account_signer2.get_public_key().as_hex() data.payload.CopyFrom(transaction_payload) data.revoked = False self.expect_get({cert_address: data}) self.expect_invalid_transaction()
def test_public_key_handler_store_already_registered_public_key(): """ Case: send transaction request to store already registered certificate public key. Expect: invalid transaction error is raised with public key is already registered error message. """ new_public_key_payload = generate_rsa_payload() transaction_payload = TransactionPayload() transaction_payload.method = PubKeyMethod.STORE transaction_payload.data = new_public_key_payload.SerializeToString() serialized_transaction_payload = transaction_payload.SerializeToString() transaction_header = generate_header(serialized_transaction_payload, INPUTS, OUTPUTS) serialized_header = transaction_header.SerializeToString() transaction_request = TpProcessRequest( header=transaction_header, payload=serialized_transaction_payload, signature=create_signer( private_key=SENDER_PRIVATE_KEY).sign(serialized_header), ) already_registered_public_key = PubKeyStorage() already_registered_public_key.owner = SENDER_PUBLIC_KEY already_registered_public_key.payload.CopyFrom(new_public_key_payload) already_registered_public_key.is_revoked = False serialized_already_registered_public_key = already_registered_public_key.SerializeToString( ) mock_context = StubContext(inputs=INPUTS, outputs=OUTPUTS, initial_state={ ADDRESS_FROM_CERTIFICATE_PUBLIC_KEY: serialized_already_registered_public_key, }) with pytest.raises(InvalidTransaction) as error: PubKeyHandler().apply(transaction=transaction_request, context=mock_context) assert 'This public key is already registered.' == str(error.value)
def test_public_key_handler_revoke_already_revoked(): """ Case: send transaction request to revoke already revoked certificate public key. Expect: invalid transaction error is raised with no certificate public key is presented in chain error message. """ revoke_public_key_payload = RevokePubKeyPayload( address=ADDRESS_FROM_CERTIFICATE_PUBLIC_KEY, ) transaction_payload = TransactionPayload() transaction_payload.method = PubKeyMethod.REVOKE transaction_payload.data = revoke_public_key_payload.SerializeToString() serialized_transaction_payload = transaction_payload.SerializeToString() transaction_header = generate_header(serialized_transaction_payload, INPUTS, OUTPUTS) serialized_header = transaction_header.SerializeToString() transaction_request = TpProcessRequest( header=transaction_header, payload=serialized_transaction_payload, signature=create_signer(private_key=SENDER_PRIVATE_KEY).sign(serialized_header), ) existing_public_key_payload = generate_rsa_payload() existing_public_key_storage = PubKeyStorage() existing_public_key_storage.owner = SENDER_PUBLIC_KEY existing_public_key_storage.payload.CopyFrom(existing_public_key_payload) existing_public_key_storage.is_revoked = True serialized_existing_public_key_storage = existing_public_key_storage.SerializeToString() mock_context = StubContext(inputs=INPUTS, outputs=OUTPUTS, initial_state={ ADDRESS_FROM_CERTIFICATE_PUBLIC_KEY: serialized_existing_public_key_storage, }) with pytest.raises(InvalidTransaction) as error: PubKeyHandler().apply(transaction=transaction_request, context=mock_context) assert 'The public key is already revoked.' == str(error.value)
def test_revoke_success(self): context = self.get_context() cert, key, key_export = PubKeyClient.create_certificate( context.pub_key_payload, org_name='different', signer=self.account_signer2) transaction_signature, cert_address, transaction_payload = self._pre_parse_payload_and_exec( context, cert, key, 'revoke') data = PubKeyStorage() data.owner = context.client.get_signer().get_public_key().as_hex() data.payload.CopyFrom(transaction_payload) data.revoked = False self.expect_get({cert_address: data}) data.revoked = True self.expect_set(transaction_signature, PubKeyMethod.REVOKE, {cert_address: data}) self.expect_ok()
def test_store_success(self): context = self.get_context() cert, key, _ = create_certificate(context.pub_key_payload, signer=context.client.get_signer()) cert_address, transaction_payload = self._pre_parse_payload_and_exec(context, cert, key) self.expect_get({cert_address: None}) account = AccountClient.get_account_model(PUB_KEY_STORE_PRICE) self.expect_get({self.account_address1: account}) data = PubKeyStorage() data.owner = self.account_signer1.get_public_key().as_hex() data.payload.CopyFrom(transaction_payload) data.revoked = False account.balance -= PUB_KEY_STORE_PRICE account.pub_keys.append(cert_address) self.expect_set({ self.account_address1: account, cert_address: data }) self.expect_ok()
def test_store_ecdsa_public_key(): """ Case: send transaction request to store certificate public key (ECDSA) for other. Expect: public key information is stored to blockchain linked to owner address. Transaction sender paid for storing. """ inputs = outputs = [ ADDRESS_FROM_ECDSA_PUBLIC_KEY, OWNER_ADDRESS, PAYER_ADDRESS, ZERO_ADDRESS, IS_NODE_ECONOMY_ENABLED_ADDRESS, ] new_public_key_payload = generate_ecdsa_payload(key=ECDSA_PUBLIC_KEY) serialized_new_public_key_payload = new_public_key_payload.SerializeToString() private_key = Secp256k1PrivateKey.from_hex(OWNER_PRIVATE_KEY) signature_by_owner = Secp256k1Context().sign(serialized_new_public_key_payload, private_key) new_public_key_store_and_pay_payload = NewPubKeyStoreAndPayPayload( pub_key_payload=new_public_key_payload, owner_public_key=bytes.fromhex(OWNER_PUBLIC_KEY), signature_by_owner=bytes.fromhex(signature_by_owner), ) transaction_payload = TransactionPayload() transaction_payload.method = PubKeyMethod.STORE_AND_PAY transaction_payload.data = new_public_key_store_and_pay_payload.SerializeToString() serialized_transaction_payload = transaction_payload.SerializeToString() transaction_header = generate_header( serialized_transaction_payload, inputs, outputs, signer_public_key=PAYER_PUBLIC_KEY, ) serialized_header = transaction_header.SerializeToString() transaction_request = TpProcessRequest( header=transaction_header, payload=serialized_transaction_payload, signature=create_signer(private_key=PAYER_PRIVATE_KEY).sign(serialized_header), ) payer_account = Account() payer_account.balance = PAYER_INITIAL_BALANCE serialized_payer_account = payer_account.SerializeToString() owner_account = Account() owner_account.pub_keys.append(RANDOM_ALREADY_STORED_OWNER_PUBLIC_KEY_ADDRESS) serialized_owner_account = owner_account.SerializeToString() zero_account = Account() zero_account.balance = 0 serialized_zero_account = zero_account.SerializeToString() mock_context = StubContext(inputs=inputs, outputs=outputs, initial_state={ OWNER_ADDRESS: serialized_owner_account, PAYER_ADDRESS: serialized_payer_account, ZERO_ADDRESS: serialized_zero_account, }) expected_public_key_storage = PubKeyStorage() expected_public_key_storage.owner = OWNER_PUBLIC_KEY expected_public_key_storage.payload.CopyFrom(new_public_key_payload) expected_public_key_storage.is_revoked = False expected_serialized_public_key_storage = expected_public_key_storage.SerializeToString() expected_payer_account = Account() expected_payer_account.balance = PAYER_INITIAL_BALANCE - PUB_KEY_STORE_PRICE serialized_expected_payer_account = expected_payer_account.SerializeToString() expected_owner_account = Account() expected_owner_account.pub_keys.append(RANDOM_ALREADY_STORED_OWNER_PUBLIC_KEY_ADDRESS) expected_owner_account.pub_keys.append(ADDRESS_FROM_ECDSA_PUBLIC_KEY) serialized_expected_owner_account = expected_owner_account.SerializeToString() expected_zero_account = Account() expected_zero_account.balance = 0 + PUB_KEY_STORE_PRICE expected_serialized_zero_account = expected_zero_account.SerializeToString() expected_state = { OWNER_ADDRESS: serialized_expected_owner_account, PAYER_ADDRESS: serialized_expected_payer_account, ADDRESS_FROM_ECDSA_PUBLIC_KEY: expected_serialized_public_key_storage, ZERO_ADDRESS: expected_serialized_zero_account, } PubKeyHandler().apply(transaction=transaction_request, context=mock_context) state_as_list = mock_context.get_state(addresses=[ OWNER_ADDRESS, PAYER_ADDRESS, ADDRESS_FROM_ECDSA_PUBLIC_KEY, ZERO_ADDRESS, ]) state_as_dict = {entry.address: entry.data for entry in state_as_list} assert expected_state == state_as_dict
def test_public_key_handler_rsa_store(): """ Case: send transaction request to store certificate public key. Expect: public key information is stored to blockchain linked to owner address. Owner paid tokens for storing. """ new_public_key_payload = generate_rsa_payload() transaction_payload = TransactionPayload() transaction_payload.method = PubKeyMethod.STORE transaction_payload.data = new_public_key_payload.SerializeToString() serialized_transaction_payload = transaction_payload.SerializeToString() transaction_header = generate_header(serialized_transaction_payload, INPUTS, OUTPUTS) serialized_header = transaction_header.SerializeToString() transaction_request = TpProcessRequest( header=transaction_header, payload=serialized_transaction_payload, signature=create_signer( private_key=SENDER_PRIVATE_KEY).sign(serialized_header), ) sender_account = Account() sender_account.balance = SENDER_INITIAL_BALANCE sender_account.pub_keys.append(RANDOM_ALREADY_STORED_SENDER_PUBLIC_KEY) serialized_sender_account = sender_account.SerializeToString() zero_account = Account() zero_account.balance = 0 serialized_zero_account = zero_account.SerializeToString() mock_context = StubContext(inputs=INPUTS, outputs=OUTPUTS, initial_state={ SENDER_ADDRESS: serialized_sender_account, ZERO_ADDRESS: serialized_zero_account, }) expected_public_key_storage = PubKeyStorage() expected_public_key_storage.owner = SENDER_PUBLIC_KEY expected_public_key_storage.payload.CopyFrom(new_public_key_payload) expected_public_key_storage.is_revoked = False expected_serialized_public_key_storage = expected_public_key_storage.SerializeToString( ) expected_sender_account = Account() expected_sender_account.balance = SENDER_INITIAL_BALANCE - PUB_KEY_STORE_PRICE expected_sender_account.pub_keys.append( RANDOM_ALREADY_STORED_SENDER_PUBLIC_KEY) expected_sender_account.pub_keys.append( ADDRESS_FROM_CERTIFICATE_PUBLIC_KEY) expected_serialized_sender_account = expected_sender_account.SerializeToString( ) expected_zero_account = Account() expected_zero_account.balance = 0 + PUB_KEY_STORE_PRICE expected_serialized_zero_account = expected_zero_account.SerializeToString( ) expected_state = { SENDER_ADDRESS: expected_serialized_sender_account, ADDRESS_FROM_CERTIFICATE_PUBLIC_KEY: expected_serialized_public_key_storage, ZERO_ADDRESS: expected_serialized_zero_account, } PubKeyHandler().apply(transaction=transaction_request, context=mock_context) state_as_list = mock_context.get_state(addresses=[ SENDER_ADDRESS, ADDRESS_FROM_CERTIFICATE_PUBLIC_KEY, ZERO_ADDRESS, ]) state_as_dict = {entry.address: entry.data for entry in state_as_list} assert expected_state == state_as_dict
def test_public_key_handler_store_sender_is_node(): """ Case: send transaction request, to store certificate public key, when sender is node (same addresses). Expect: public key information is stored to blockchain linked to owner address. Owner hasn't paid for storing. """ new_public_key_payload = generate_rsa_payload() transaction_payload = TransactionPayload() transaction_payload.method = PubKeyMethod.STORE transaction_payload.data = new_public_key_payload.SerializeToString() serialized_transaction_payload = transaction_payload.SerializeToString() transaction_header = generate_header(serialized_transaction_payload, INPUTS, OUTPUTS) serialized_header = transaction_header.SerializeToString() transaction_request = TpProcessRequest( header=transaction_header, payload=serialized_transaction_payload, signature=create_signer( private_key=SENDER_PRIVATE_KEY).sign(serialized_header), ) sender_account = Account() sender_account.pub_keys.append(RANDOM_ALREADY_STORED_SENDER_PUBLIC_KEY) serialized_sender_account = sender_account.SerializeToString() zero_account = Account() serialized_zero_account = zero_account.SerializeToString() is_economy_enabled_setting = Setting() is_economy_enabled_setting.entries.add(key='remme.economy_enabled', value='false') serialized_is_economy_enabled_setting = is_economy_enabled_setting.SerializeToString( ) mock_context = StubContext(inputs=INPUTS, outputs=OUTPUTS, initial_state={ SENDER_ADDRESS: serialized_sender_account, IS_NODE_ECONOMY_ENABLED_ADDRESS: serialized_is_economy_enabled_setting, ZERO_ADDRESS: serialized_zero_account, }) expected_public_key_storage = PubKeyStorage() expected_public_key_storage.owner = SENDER_PUBLIC_KEY expected_public_key_storage.payload.CopyFrom(new_public_key_payload) expected_public_key_storage.is_revoked = False expected_serialized_public_key_storage = expected_public_key_storage.SerializeToString( ) expected_sender_account = Account() expected_sender_account.pub_keys.append( RANDOM_ALREADY_STORED_SENDER_PUBLIC_KEY) expected_sender_account.pub_keys.append( ADDRESS_FROM_CERTIFICATE_PUBLIC_KEY) expected_serialized_sender_account = expected_sender_account.SerializeToString( ) expected_zero_account = Account() expected_serialized_zero_account = expected_zero_account.SerializeToString( ) expected_state = { SENDER_ADDRESS: expected_serialized_sender_account, ADDRESS_FROM_CERTIFICATE_PUBLIC_KEY: expected_serialized_public_key_storage, ZERO_ADDRESS: expected_serialized_zero_account, } PubKeyHandler().apply(transaction=transaction_request, context=mock_context) state_as_list = mock_context.get_state(addresses=[ SENDER_ADDRESS, ADDRESS_FROM_CERTIFICATE_PUBLIC_KEY, ZERO_ADDRESS, ]) state_as_dict = {entry.address: entry.data for entry in state_as_list} assert expected_state == state_as_dict
def _store_public_key_for_other(self, context, signer_pubkey, transaction_payload): """ Store public key for other account. The transaction for account which want to pay for other account public keys storing. A first account -> send payload -> A second account -> send transaction with first account's public key, but sign and pay for storing on own -> Remme-core. So Remme core charges tokens from a second account, but store a first account's public key. Public key owner here is a first account. Arguments: context (sawtooth_sdk.processor.context): context to store updated state (blockchain data). signer_pubkey: transaction sender public key. transaction_payload (pub_key_pb2.NewPubKeyStoreAndPayPayload): payload for storing public key for other. """ new_public_key_payload = transaction_payload.pub_key_payload owner_public_key_as_bytes = transaction_payload.owner_public_key owner_public_key_as_hex = owner_public_key_as_bytes.hex() owner_secp256k1_public_key = Secp256k1PublicKey.from_hex(owner_public_key_as_hex) is_owner_public_key_payload_signature_valid = Secp256k1Context().verify( signature=transaction_payload.signature_by_owner.hex(), message=new_public_key_payload.SerializeToString(), public_key=owner_secp256k1_public_key, ) if not is_owner_public_key_payload_signature_valid: raise InvalidTransaction('Public key owner\'s signature is invalid.') processor = self._get_public_key_processor(transaction_payload=transaction_payload.pub_key_payload) if not processor.verify(): raise InvalidTransaction('Payed public key has invalid signature.') public_key = processor.get_public_key() public_key_to_store_address = self.make_address_from_data(public_key) public_key_to_store_owner_address = AccountHandler().make_address_from_data(owner_public_key_as_hex) payer_for_storing_address = AccountHandler().make_address_from_data(signer_pubkey) public_key_information, public_key_to_store_owner_account, payer_for_storing_account = get_multiple_data(context, [ (public_key_to_store_address, PubKeyStorage), (public_key_to_store_owner_address, Account), (payer_for_storing_address, Account), ]) if public_key_information: raise InvalidTransaction('This public key is already registered.') if public_key_to_store_owner_account is None: public_key_to_store_owner_account = Account() if payer_for_storing_account is None: payer_for_storing_account = Account() if not self._is_public_key_validity_exceeded( valid_from=new_public_key_payload.valid_from, valid_to=new_public_key_payload.valid_to, ): raise InvalidTransaction('The public key validity exceeds the maximum value.') public_key_information = PubKeyStorage() public_key_information.owner = owner_public_key_as_hex public_key_information.payload.CopyFrom(new_public_key_payload) public_key_information.is_revoked = False state = { public_key_to_store_owner_address: public_key_to_store_owner_account, payer_for_storing_address: payer_for_storing_account, public_key_to_store_address: public_key_information, } charging_state = self._charge_for_storing(context=context, address_from=payer_for_storing_address) if charging_state is not None: state.update(charging_state) public_key_to_store_owner_account = self._store_public_key_to_account( public_key_to_store_address=public_key_to_store_address, public_key_to_store_owner_account=public_key_to_store_owner_account, ) state.update({ public_key_to_store_owner_address: public_key_to_store_owner_account, }) return state
def _store_pub_key(self, context, signer_pubkey, transaction_payload): """ Store public key to the blockchain. Flow on client: 1. Create private and public key (for instance, RSA). 2. Create random data and sign it with private key to allows node verify signature, so ensure the address sent transaction is a real owner of public key. 3. Send public key, signature, and other information to the node. Node does checks: if public key already exists in the blockchain, try to deserialize public key, try to verify signature, if validity exceeds. If transaction successfully passed checks, node charges fixed tokens price for storing public keys (if node economy is enabled) and link public key to the account (address). References: - https://docs.remme.io/remme-core/docs/family-pub-key.html - https://github.com/Remmeauth/remme-client-python/blob/develop/remme/remme_public_key_storage.py """ processor = self._get_public_key_processor(transaction_payload=transaction_payload) if not processor.verify(): raise InvalidTransaction('Invalid signature') public_key = processor.get_public_key() public_key_to_store_address = self.make_address_from_data(public_key) sender_account_address = AccountHandler().make_address_from_data(signer_pubkey) public_key_information, sender_account = get_multiple_data(context, [ (public_key_to_store_address, PubKeyStorage), (sender_account_address, Account), ]) if public_key_information: raise InvalidTransaction('This public key is already registered.') if not sender_account: sender_account = Account() if not self._is_public_key_validity_exceeded( valid_from=transaction_payload.valid_from, valid_to=transaction_payload.valid_to, ): raise InvalidTransaction('The public key validity exceeds the maximum value.') public_key_information = PubKeyStorage() public_key_information.owner = signer_pubkey public_key_information.payload.CopyFrom(transaction_payload) public_key_information.is_revoked = False state = { sender_account_address: sender_account, public_key_to_store_address: public_key_information, } charging_state = self._charge_for_storing(context=context, address_from=sender_account_address) if charging_state is not None: state.update(charging_state) sender_account = state.get(sender_account_address) sender_account = self._store_public_key_to_account( public_key_to_store_address=public_key_to_store_address, public_key_to_store_owner_account=sender_account, ) state.update({ sender_account_address: sender_account, }) return state
def test_store_rsa_public_key_no_owner_account(): """ Case: send transaction request, to store certificate public key (RSA) for other, when owner account does not exist. Expect: public key information is stored to blockchain linked to the newly created owner account's address. """ new_public_key_payload = generate_rsa_payload(key=CERTIFICATE_PUBLIC_KEY) serialized_new_public_key_payload = new_public_key_payload.SerializeToString() private_key = Secp256k1PrivateKey.from_hex(OWNER_PRIVATE_KEY) signature_by_owner = Secp256k1Context().sign(serialized_new_public_key_payload, private_key) new_public_key_store_and_pay_payload = NewPubKeyStoreAndPayPayload( pub_key_payload=new_public_key_payload, owner_public_key=bytes.fromhex(OWNER_PUBLIC_KEY), signature_by_owner=bytes.fromhex(signature_by_owner), ) transaction_payload = TransactionPayload() transaction_payload.method = PubKeyMethod.STORE_AND_PAY transaction_payload.data = new_public_key_store_and_pay_payload.SerializeToString() serialized_transaction_payload = transaction_payload.SerializeToString() transaction_header = generate_header( serialized_transaction_payload, INPUTS, OUTPUTS, signer_public_key=PAYER_PUBLIC_KEY, ) serialized_header = transaction_header.SerializeToString() transaction_request = TpProcessRequest( header=transaction_header, payload=serialized_transaction_payload, signature=create_signer(private_key=PAYER_PRIVATE_KEY).sign(serialized_header), ) payer_account = Account() payer_account.balance = PAYER_INITIAL_BALANCE serialized_payer_account = payer_account.SerializeToString() zero_account = Account() zero_account.balance = 0 serialized_zero_account = zero_account.SerializeToString() mock_context = StubContext(inputs=INPUTS, outputs=OUTPUTS, initial_state={ PAYER_ADDRESS: serialized_payer_account, ZERO_ADDRESS: serialized_zero_account, }) expected_public_key_storage = PubKeyStorage() expected_public_key_storage.owner = OWNER_PUBLIC_KEY expected_public_key_storage.payload.CopyFrom(new_public_key_payload) expected_public_key_storage.is_revoked = False expected_serialized_public_key_storage = expected_public_key_storage.SerializeToString() expected_payer_account = Account() expected_payer_account.balance = PAYER_INITIAL_BALANCE - PUB_KEY_STORE_PRICE serialized_expected_payer_account = expected_payer_account.SerializeToString() expected_owner_account = Account() expected_owner_account.pub_keys.append(ADDRESS_FROM_CERTIFICATE_PUBLIC_KEY) serialized_expected_owner_account = expected_owner_account.SerializeToString() expected_zero_account = Account() expected_zero_account.balance = 0 + PUB_KEY_STORE_PRICE expected_serialized_zero_account = expected_zero_account.SerializeToString() expected_state = { OWNER_ADDRESS: serialized_expected_owner_account, PAYER_ADDRESS: serialized_expected_payer_account, ADDRESS_FROM_CERTIFICATE_PUBLIC_KEY: expected_serialized_public_key_storage, ZERO_ADDRESS: expected_serialized_zero_account, } PubKeyHandler().apply(transaction=transaction_request, context=mock_context) state_as_list = mock_context.get_state(addresses=[ OWNER_ADDRESS, PAYER_ADDRESS, ADDRESS_FROM_CERTIFICATE_PUBLIC_KEY, ZERO_ADDRESS, ]) state_as_dict = {entry.address: entry.data for entry in state_as_list} assert expected_state == state_as_dict
def test_store_public_key_for_other_economy_is_not_enabled(): """ Case: send transaction request, to store certificate public key for other, when economy isn't enabled. Expect: public key information is stored to blockchain linked to owner address. Owner hasn't paid for storing. """ new_public_key_payload = generate_rsa_payload(key=CERTIFICATE_PUBLIC_KEY) serialized_new_public_key_payload = new_public_key_payload.SerializeToString() private_key = Secp256k1PrivateKey.from_hex(OWNER_PRIVATE_KEY) signature_by_owner = Secp256k1Context().sign(serialized_new_public_key_payload, private_key) new_public_key_store_and_pay_payload = NewPubKeyStoreAndPayPayload( pub_key_payload=new_public_key_payload, owner_public_key=bytes.fromhex(OWNER_PUBLIC_KEY), signature_by_owner=bytes.fromhex(signature_by_owner), ) transaction_payload = TransactionPayload() transaction_payload.method = PubKeyMethod.STORE_AND_PAY transaction_payload.data = new_public_key_store_and_pay_payload.SerializeToString() serialized_transaction_payload = transaction_payload.SerializeToString() transaction_header = generate_header( serialized_transaction_payload, INPUTS, OUTPUTS, signer_public_key=PAYER_PUBLIC_KEY, ) serialized_header = transaction_header.SerializeToString() transaction_request = TpProcessRequest( header=transaction_header, payload=serialized_transaction_payload, signature=create_signer(private_key=PAYER_PRIVATE_KEY).sign(serialized_header), ) payer_account = Account() payer_account.balance = PAYER_INITIAL_BALANCE serialized_payer_account = payer_account.SerializeToString() owner_account = Account() owner_account.pub_keys.append(RANDOM_ALREADY_STORED_OWNER_PUBLIC_KEY_ADDRESS) serialized_owner_account = owner_account.SerializeToString() zero_account = Account() zero_account.balance = 0 serialized_zero_account = zero_account.SerializeToString() is_economy_enabled_setting = Setting() is_economy_enabled_setting.entries.add(key='remme.economy_enabled', value='false') serialized_is_economy_enabled_setting = is_economy_enabled_setting.SerializeToString() mock_context = StubContext(inputs=INPUTS, outputs=OUTPUTS, initial_state={ OWNER_ADDRESS: serialized_owner_account, PAYER_ADDRESS: serialized_payer_account, ZERO_ADDRESS: serialized_zero_account, IS_NODE_ECONOMY_ENABLED_ADDRESS: serialized_is_economy_enabled_setting, }) expected_public_key_storage = PubKeyStorage() expected_public_key_storage.owner = OWNER_PUBLIC_KEY expected_public_key_storage.payload.CopyFrom(new_public_key_payload) expected_public_key_storage.is_revoked = False expected_serialized_public_key_storage = expected_public_key_storage.SerializeToString() expected_payer_account = Account() expected_payer_account.balance = PAYER_INITIAL_BALANCE serialized_expected_payer_account = expected_payer_account.SerializeToString() expected_owner_account = Account() expected_owner_account.pub_keys.append(RANDOM_ALREADY_STORED_OWNER_PUBLIC_KEY_ADDRESS) expected_owner_account.pub_keys.append(ADDRESS_FROM_CERTIFICATE_PUBLIC_KEY) serialized_expected_owner_account = expected_owner_account.SerializeToString() expected_zero_account = Account() expected_zero_account.balance = 0 expected_serialized_zero_account = expected_zero_account.SerializeToString() expected_state = { OWNER_ADDRESS: serialized_expected_owner_account, PAYER_ADDRESS: serialized_expected_payer_account, ADDRESS_FROM_CERTIFICATE_PUBLIC_KEY: expected_serialized_public_key_storage, ZERO_ADDRESS: expected_serialized_zero_account, } PubKeyHandler().apply(transaction=transaction_request, context=mock_context) state_as_list = mock_context.get_state(addresses=[ OWNER_ADDRESS, PAYER_ADDRESS, ADDRESS_FROM_CERTIFICATE_PUBLIC_KEY, ZERO_ADDRESS, ]) state_as_dict = {entry.address: entry.data for entry in state_as_list} assert expected_state == state_as_dict