示例#1
0
def test_approve_atomic_swap_without_secret_lock():
    """
    Case: approve atomic swap without set secret lock.
    Expect: invalid transaction error is raised with only transaction initiator may approve the swap error message.
    """
    atomic_swap_approve_payload = AtomicSwapApprovePayload(swap_id=SWAP_ID, )

    transaction_payload = TransactionPayload()
    transaction_payload.method = AtomicSwapMethod.APPROVE
    transaction_payload.data = atomic_swap_approve_payload.SerializeToString()

    serialized_transaction_payload = transaction_payload.SerializeToString()

    transaction_header = TransactionHeader(
        signer_public_key=BOT_PUBLIC_KEY,
        family_name=TRANSACTION_REQUEST_ACCOUNT_HANDLER_PARAMS.get(
            'family_name'),
        family_version=TRANSACTION_REQUEST_ACCOUNT_HANDLER_PARAMS.get(
            'family_version'),
        inputs=INPUTS,
        outputs=OUTPUTS,
        dependencies=[],
        payload_sha512=hash512(data=serialized_transaction_payload),
        batcher_public_key=RANDOM_NODE_PUBLIC_KEY,
        nonce=time.time().hex().encode(),
    )

    serialized_header = transaction_header.SerializeToString()

    transaction_request = TpProcessRequest(
        header=transaction_header,
        payload=serialized_transaction_payload,
        signature=create_signer(
            private_key=BOT_PRIVATE_KEY).sign(serialized_header),
    )

    swap_info = AtomicSwapInfo()
    swap_info.swap_id = SWAP_ID
    swap_info.state = AtomicSwapInfo.OPENED
    swap_info.amount = TOKENS_AMOUNT_TO_SWAP
    swap_info.created_at = CURRENT_TIMESTAMP
    swap_info.sender_address = BOT_ADDRESS
    swap_info.receiver_address = ALICE_ADDRESS
    swap_info.is_initiator = True
    serialized_swap_info = swap_info.SerializeToString()

    mock_context = StubContext(inputs=INPUTS,
                               outputs=OUTPUTS,
                               initial_state={
                                   ADDRESS_TO_STORE_SWAP_INFO_BY:
                                   serialized_swap_info,
                               })

    with pytest.raises(InvalidTransaction) as error:
        AtomicSwapHandler().apply(transaction=transaction_request,
                                  context=mock_context)

    assert 'Secret lock is needed for Bob to provide a secret key.' == str(
        error.value)
示例#2
0
def test_close_already_closed_atomic_swap():
    """
    Case: close already closed atomic swap.
    Expect: invalid transaction error is raised with already operation with closed or expired swap error message.
    """
    atomic_swap_close_payload = AtomicSwapClosePayload(
        swap_id=SWAP_ID,
        secret_key=SECRET_KEY,
    )

    transaction_payload = TransactionPayload()
    transaction_payload.method = AtomicSwapMethod.CLOSE
    transaction_payload.data = atomic_swap_close_payload.SerializeToString()

    serialized_transaction_payload = transaction_payload.SerializeToString()

    transaction_header = TransactionHeader(
        signer_public_key=BOT_PUBLIC_KEY,
        family_name=TRANSACTION_REQUEST_ACCOUNT_HANDLER_PARAMS.get(
            'family_name'),
        family_version=TRANSACTION_REQUEST_ACCOUNT_HANDLER_PARAMS.get(
            'family_version'),
        inputs=INPUTS,
        outputs=OUTPUTS,
        dependencies=[],
        payload_sha512=hash512(data=serialized_transaction_payload),
        batcher_public_key=RANDOM_NODE_PUBLIC_KEY,
        nonce=time.time().hex().encode(),
    )

    serialized_header = transaction_header.SerializeToString()

    transaction_request = TpProcessRequest(
        header=transaction_header,
        payload=serialized_transaction_payload,
        signature=create_signer(
            private_key=BOT_PRIVATE_KEY).sign(serialized_header),
    )

    already_closed_swap_info = AtomicSwapInfo()
    already_closed_swap_info.swap_id = SWAP_ID
    already_closed_swap_info.state = AtomicSwapInfo.CLOSED
    serialized_already_closed_swap_info = already_closed_swap_info.SerializeToString(
    )

    mock_context = StubContext(inputs=INPUTS,
                               outputs=OUTPUTS,
                               initial_state={
                                   ADDRESS_TO_STORE_SWAP_INFO_BY:
                                   serialized_already_closed_swap_info,
                               })

    with pytest.raises(InvalidTransaction) as error:
        AtomicSwapHandler().apply(transaction=transaction_request,
                                  context=mock_context)

    assert f'No operations can be done upon the swap: {SWAP_ID} as it is already closed or expired.' == str(
        error.value)
示例#3
0
def test_close_atomic_swap_before_approve():
    """
    Case: close swap before it is approved.
    Expect: invalid transaction error is raised with transaction cannot be closed before it is approved error message.
    """
    atomic_swap_close_payload = AtomicSwapClosePayload(
        swap_id=SWAP_ID,
        secret_key=SECRET_KEY,
    )

    transaction_payload = TransactionPayload()
    transaction_payload.method = AtomicSwapMethod.CLOSE
    transaction_payload.data = atomic_swap_close_payload.SerializeToString()

    serialized_transaction_payload = transaction_payload.SerializeToString()

    transaction_header = TransactionHeader(
        signer_public_key=BOT_PUBLIC_KEY,
        family_name=TRANSACTION_REQUEST_ACCOUNT_HANDLER_PARAMS.get(
            'family_name'),
        family_version=TRANSACTION_REQUEST_ACCOUNT_HANDLER_PARAMS.get(
            'family_version'),
        inputs=INPUTS,
        outputs=OUTPUTS,
        dependencies=[],
        payload_sha512=hash512(data=serialized_transaction_payload),
        batcher_public_key=RANDOM_NODE_PUBLIC_KEY,
        nonce=time.time().hex().encode(),
    )

    serialized_header = transaction_header.SerializeToString()

    transaction_request = TpProcessRequest(
        header=transaction_header,
        payload=serialized_transaction_payload,
        signature=create_signer(
            private_key=BOT_PRIVATE_KEY).sign(serialized_header),
    )

    existing_swap_info_to_close = AtomicSwapInfo()
    existing_swap_info_to_close.swap_id = SWAP_ID
    existing_swap_info_to_close.state = AtomicSwapInfo.OPENED
    existing_swap_info_to_close.secret_lock = SECRET_LOCK
    existing_swap_info_to_close.is_initiator = True
    serialized_existing_swap_info_to_lock = existing_swap_info_to_close.SerializeToString(
    )

    mock_context = StubContext(inputs=INPUTS,
                               outputs=OUTPUTS,
                               initial_state={
                                   ADDRESS_TO_STORE_SWAP_INFO_BY:
                                   serialized_existing_swap_info_to_lock,
                               })

    with pytest.raises(InvalidTransaction) as error:
        AtomicSwapHandler().apply(transaction=transaction_request,
                                  context=mock_context)

    assert 'Transaction cannot be closed before it\'s approved.' == str(
        error.value)
示例#4
0
def test_set_lock_to_atomic_swap_with_set_lock():
    """
    Case: set secret lock to atomic swap with already set secret lock.
    Expect: invalid transaction error is raised with secret lock is already added error message.
    """
    atomic_swap_init_payload = AtomicSwapSetSecretLockPayload(
        swap_id=SWAP_ID,
        secret_lock=SECRET_LOCK,
    )

    transaction_payload = TransactionPayload()
    transaction_payload.method = AtomicSwapMethod.SET_SECRET_LOCK
    transaction_payload.data = atomic_swap_init_payload.SerializeToString()

    serialized_transaction_payload = transaction_payload.SerializeToString()

    transaction_header = TransactionHeader(
        signer_public_key=BOT_PUBLIC_KEY,
        family_name=TRANSACTION_REQUEST_ACCOUNT_HANDLER_PARAMS.get(
            'family_name'),
        family_version=TRANSACTION_REQUEST_ACCOUNT_HANDLER_PARAMS.get(
            'family_version'),
        inputs=INPUTS,
        outputs=OUTPUTS,
        dependencies=[],
        payload_sha512=hash512(data=serialized_transaction_payload),
        batcher_public_key=RANDOM_NODE_PUBLIC_KEY,
        nonce=time.time().hex().encode(),
    )

    serialized_header = transaction_header.SerializeToString()

    transaction_request = TpProcessRequest(
        header=transaction_header,
        payload=serialized_transaction_payload,
        signature=create_signer(
            private_key=BOT_PRIVATE_KEY).sign(serialized_header),
    )

    already_set_lock_swap_info = AtomicSwapInfo()
    already_set_lock_swap_info.swap_id = SWAP_ID
    already_set_lock_swap_info.state = AtomicSwapInfo.OPENED
    already_set_lock_swap_info.secret_lock = SECRET_LOCK
    serialized_already_set_lock_swap_info = already_set_lock_swap_info.SerializeToString(
    )

    mock_context = StubContext(inputs=INPUTS,
                               outputs=OUTPUTS,
                               initial_state={
                                   ADDRESS_TO_STORE_SWAP_INFO_BY:
                                   serialized_already_set_lock_swap_info,
                               })

    with pytest.raises(InvalidTransaction) as error:
        AtomicSwapHandler().apply(transaction=transaction_request,
                                  context=mock_context)

    assert f'Secret lock is already added for {SWAP_ID}.' == str(error.value)
示例#5
0
def test_expire_atomic_swap_by_not_swap_owner():
    """
    Case: to expire atomic swap by signer address isn't specified in atomic swap sender address.
    Expect: invalid transaction error is raised with signer is not the one who opened the swap. error message.
    """
    atomic_swap_close_payload = AtomicSwapExpirePayload(swap_id=SWAP_ID, )

    transaction_payload = TransactionPayload()
    transaction_payload.method = AtomicSwapMethod.EXPIRE
    transaction_payload.data = atomic_swap_close_payload.SerializeToString()

    serialized_transaction_payload = transaction_payload.SerializeToString()

    transaction_header = TransactionHeader(
        signer_public_key=BOT_PUBLIC_KEY,
        family_name=TRANSACTION_REQUEST_ACCOUNT_HANDLER_PARAMS.get(
            'family_name'),
        family_version=TRANSACTION_REQUEST_ACCOUNT_HANDLER_PARAMS.get(
            'family_version'),
        inputs=INPUTS,
        outputs=OUTPUTS,
        dependencies=[],
        payload_sha512=hash512(data=serialized_transaction_payload),
        batcher_public_key=RANDOM_NODE_PUBLIC_KEY,
        nonce=time.time().hex().encode(),
    )

    serialized_header = transaction_header.SerializeToString()

    transaction_request = TpProcessRequest(
        header=transaction_header,
        payload=serialized_transaction_payload,
        signature=create_signer(
            private_key=BOT_PRIVATE_KEY).sign(serialized_header),
    )

    existing_swap_info_to_expire = AtomicSwapInfo()
    existing_swap_info_to_expire.swap_id = SWAP_ID
    existing_swap_info_to_expire.state = AtomicSwapInfo.OPENED
    existing_swap_info_to_expire.sender_address = RANDOM_ADDRESS
    serialized_existing_swap_info_to_expire = existing_swap_info_to_expire.SerializeToString(
    )

    mock_context = StubContext(inputs=INPUTS,
                               outputs=OUTPUTS,
                               initial_state={
                                   ADDRESS_TO_STORE_SWAP_INFO_BY:
                                   serialized_existing_swap_info_to_expire,
                               })

    with pytest.raises(InvalidTransaction) as error:
        AtomicSwapHandler().apply(transaction=transaction_request,
                                  context=mock_context)

    assert f'Signer is not the one who opened the swap.' == str(error.value)
示例#6
0
def test_expire_atomic_swap():
    """
    Case: to expire atomic swap.
    Expect: increase bot address balance by swap amount. Leave commission on zero address.
    """
    atomic_swap_expire_payload = AtomicSwapExpirePayload(swap_id=SWAP_ID, )

    transaction_payload = TransactionPayload()
    transaction_payload.method = AtomicSwapMethod.EXPIRE
    transaction_payload.data = atomic_swap_expire_payload.SerializeToString()

    serialized_transaction_payload = transaction_payload.SerializeToString()

    transaction_header = TransactionHeader(
        signer_public_key=BOT_PUBLIC_KEY,
        family_name=TRANSACTION_REQUEST_ACCOUNT_HANDLER_PARAMS.get(
            'family_name'),
        family_version=TRANSACTION_REQUEST_ACCOUNT_HANDLER_PARAMS.get(
            'family_version'),
        inputs=INPUTS,
        outputs=OUTPUTS,
        dependencies=[],
        payload_sha512=hash512(data=serialized_transaction_payload),
        batcher_public_key=RANDOM_NODE_PUBLIC_KEY,
        nonce=time.time().hex().encode(),
    )

    serialized_header = transaction_header.SerializeToString()

    transaction_request = TpProcessRequest(
        header=transaction_header,
        payload=serialized_transaction_payload,
        signature=create_signer(
            private_key=BOT_PRIVATE_KEY).sign(serialized_header),
    )

    bot_account = Account()
    bot_account.balance = 4700
    serialized_bot_account = bot_account.SerializeToString()

    genesis_members_setting = Setting()
    genesis_members_setting.entries.add(key=SETTINGS_KEY_ZERO_ADDRESS_OWNERS,
                                        value=f'{BOT_PUBLIC_KEY},')
    serialized_genesis_members_setting = genesis_members_setting.SerializeToString(
    )

    existing_swap_info = AtomicSwapInfo()
    existing_swap_info.swap_id = SWAP_ID
    existing_swap_info.state = AtomicSwapInfo.OPENED
    existing_swap_info.amount = TOKENS_AMOUNT_TO_SWAP
    existing_swap_info.created_at = CURRENT_TIMESTAMP // 2
    existing_swap_info.sender_address = BOT_ADDRESS
    existing_swap_info.receiver_address = ALICE_ADDRESS
    existing_swap_info.is_initiator = True
    serialized_existing_swap_info = existing_swap_info.SerializeToString()

    mock_context = StubContext(inputs=INPUTS,
                               outputs=OUTPUTS,
                               initial_state={
                                   BLOCK_INFO_CONFIG_ADDRESS:
                                   SERIALIZED_BLOCK_INFO_CONFIG,
                                   BLOCK_INFO_ADDRESS:
                                   SERIALIZED_BLOCK_INFO,
                                   BOT_ADDRESS:
                                   serialized_bot_account,
                                   ADDRESS_TO_STORE_SWAP_INFO_BY:
                                   serialized_existing_swap_info,
                                   ADDRESS_TO_GET_GENESIS_MEMBERS_AS_STRING_BY:
                                   serialized_genesis_members_setting,
                               })

    expected_bot_account = Account()
    expected_bot_account.balance = 4700 + TOKENS_AMOUNT_TO_SWAP
    serialized_expected_bot_account = expected_bot_account.SerializeToString()

    expected_swap_info = AtomicSwapInfo()
    expected_swap_info.swap_id = SWAP_ID
    expected_swap_info.state = AtomicSwapInfo.EXPIRED
    expected_swap_info.amount = TOKENS_AMOUNT_TO_SWAP
    expected_swap_info.created_at = CURRENT_TIMESTAMP // 2
    expected_swap_info.sender_address = BOT_ADDRESS
    expected_swap_info.receiver_address = ALICE_ADDRESS
    expected_swap_info.is_initiator = True
    serialized_expected_swap_info = expected_swap_info.SerializeToString()

    expected_state = {
        BOT_ADDRESS: serialized_expected_bot_account,
        ADDRESS_TO_STORE_SWAP_INFO_BY: serialized_expected_swap_info,
    }

    AtomicSwapHandler().apply(transaction=transaction_request,
                              context=mock_context)

    state_as_list = mock_context.get_state(addresses=[
        ADDRESS_TO_STORE_SWAP_INFO_BY,
        BOT_ADDRESS,
    ])
    state_as_dict = {entry.address: entry.data for entry in state_as_list}

    assert expected_state == state_as_dict
示例#7
0
    def get_context(self):
        context = super().get_context()
        context.AMOUNT = 10000
        context.COMMISSION = 100

        context.swap_id = generate_random_key()
        context.swap_address = AtomicSwapHandler.make_address_from_data(
            context.swap_id)
        context.secret_key = generate_random_key()
        context.secret_lock = hash256(context.secret_key)
        context.now = datetime.datetime.now()
        context.created_at = int(context.now.timestamp())
        context.email_address = ""
        context.sender_address_non_local = ""

        swap_info = AtomicSwapInfo()
        swap_info.swap_id = context.swap_id
        swap_info.is_closed = False
        swap_info.is_approved = True
        swap_info.is_initiator = False
        swap_info.amount = context.AMOUNT
        swap_info.created_at = context.created_at
        swap_info.email_address_encrypted_optional = context.email_address
        swap_info.sender_address = self.account_address1
        swap_info.sender_address_non_local = context.sender_address_non_local
        swap_info.receiver_address = self.account_address2
        swap_info.secret_lock = context.secret_lock
        context.swap_info = swap_info
        return context
示例#8
0
    def _swap_init(self, context, signer_pubkey, swap_init_payload):
        """
        if SecretLockOptionalBob is provided, Bob uses _swap_init to respond to requested swap
        Otherwise, Alice uses _swap_init to request a swap and thus, Bob can't receive funds until Alice "approves".
        """
        LOGGER.info("0. Check if swap ID already exists")
        # 0. Check if swap ID already exists
        if self.get_swap_info_from_swap_id(context,
                                           swap_init_payload.swap_id,
                                           to_raise_exception=False):
            raise InvalidTransaction(
                'Atomic swap ID has already been taken, please use a different one!'
            )
        # END

        swap_info = AtomicSwapInfo()
        swap_info.swap_id = swap_init_payload.swap_id
        swap_info.state = AtomicSwapInfo.OPENED
        swap_info.amount = swap_init_payload.amount
        swap_info.created_at = swap_init_payload.created_at
        swap_info.email_address_encrypted_optional = swap_init_payload.email_address_encrypted_by_initiator
        swap_info.sender_address = AccountHandler().make_address_from_data(
            signer_pubkey)
        swap_info.sender_address_non_local = swap_init_payload.sender_address_non_local
        swap_info.receiver_address = swap_init_payload.receiver_address

        if not AccountHandler().is_handler_address(swap_info.receiver_address):
            raise InvalidTransaction(
                'Receiver address is not of a Token type.')

        LOGGER.info("1. Ensure transaction initiated within an hour")
        # 1. Ensure transaction initiated within an hour
        swap_info.secret_lock = swap_init_payload.secret_lock_by_solicitor
        created_at = self.get_datetime_from_timestamp(swap_info.created_at)
        now = datetime.datetime.utcnow()

        if not (now - datetime.timedelta(hours=1) < created_at < now):
            raise InvalidTransaction(
                'Transaction is created a long time ago or timestamp is assigned set.'
            )
        # END

        LOGGER.info("2. Check weather the sender is Alice")
        # 2. Check weather the sender is Alice:
        swap_info.is_initiator = not swap_init_payload.secret_lock_by_solicitor
        # END

        # 3. Transfer funds to zero address.
        LOGGER.info("3. Transfer funds to zero address")
        commission = int(_get_setting_value(context, SETTINGS_SWAP_COMMISSION))
        if commission < 0:
            raise InvalidTransaction('Wrong commission address.')
        LOGGER.info("4. Get sender's account {}".format(
            swap_info.sender_address))
        account = get_account_by_address(context, swap_info.sender_address)
        total_amount = swap_info.amount + commission
        if account.balance < total_amount:
            raise InvalidTransaction(
                'Not enough balance to perform the transaction in '
                'the amount (with a commission) {}.'.format(total_amount))

        transfer_payload = AccountClient.get_transfer_payload(
            ZERO_ADDRESS, total_amount)
        token_updated_state = AccountHandler()._transfer_from_address(
            context, swap_info.sender_address, transfer_payload)
        LOGGER.info("Save state")

        return {**self.get_state_update(swap_info), **token_updated_state}
示例#9
0
def test_close_atomic_swap():
    """
    Case: close atomic swap.
    Expect: increase Alice account address by swap amount.
    """
    atomic_swap_close_payload = AtomicSwapClosePayload(
        swap_id=SWAP_ID,
        secret_key=SECRET_KEY,
    )

    transaction_payload = TransactionPayload()
    transaction_payload.method = AtomicSwapMethod.CLOSE
    transaction_payload.data = atomic_swap_close_payload.SerializeToString()

    serialized_transaction_payload = transaction_payload.SerializeToString()

    transaction_header = TransactionHeader(
        signer_public_key=BOT_PUBLIC_KEY,
        family_name=TRANSACTION_REQUEST_ACCOUNT_HANDLER_PARAMS.get(
            'family_name'),
        family_version=TRANSACTION_REQUEST_ACCOUNT_HANDLER_PARAMS.get(
            'family_version'),
        inputs=INPUTS,
        outputs=OUTPUTS,
        dependencies=[],
        payload_sha512=hash512(data=serialized_transaction_payload),
        batcher_public_key=RANDOM_NODE_PUBLIC_KEY,
        nonce=time.time().hex().encode(),
    )

    serialized_header = transaction_header.SerializeToString()

    transaction_request = TpProcessRequest(
        header=transaction_header,
        payload=serialized_transaction_payload,
        signature=create_signer(
            private_key=BOT_PRIVATE_KEY).sign(serialized_header),
    )

    alice_account = Account()
    alice_account.balance = 0
    serialized_alice_account = alice_account.SerializeToString()

    existing_swap_info_to_close = AtomicSwapInfo()
    existing_swap_info_to_close.swap_id = SWAP_ID
    existing_swap_info_to_close.amount = 200
    existing_swap_info_to_close.state = AtomicSwapInfo.APPROVED
    existing_swap_info_to_close.secret_lock = SECRET_LOCK
    existing_swap_info_to_close.is_initiator = True
    existing_swap_info_to_close.sender_address = BOT_ADDRESS
    existing_swap_info_to_close.receiver_address = ALICE_ADDRESS
    serialized_existing_swap_info_to_lock = existing_swap_info_to_close.SerializeToString(
    )

    genesis_members_setting = Setting()
    genesis_members_setting.entries.add(key=SETTINGS_KEY_ZERO_ADDRESS_OWNERS,
                                        value=f'{BOT_PUBLIC_KEY},')
    serialized_genesis_members_setting = genesis_members_setting.SerializeToString(
    )

    mock_context = StubContext(inputs=INPUTS,
                               outputs=OUTPUTS,
                               initial_state={
                                   ADDRESS_TO_GET_GENESIS_MEMBERS_AS_STRING_BY:
                                   serialized_genesis_members_setting,
                                   ADDRESS_TO_STORE_SWAP_INFO_BY:
                                   serialized_existing_swap_info_to_lock,
                                   ALICE_ADDRESS: serialized_alice_account,
                               })

    expected_alice_account = Account()
    expected_alice_account.balance = TOKENS_AMOUNT_TO_SWAP
    serialized_expected_alice_account = expected_alice_account.SerializeToString(
    )

    expected_closed_swap_info = AtomicSwapInfo()
    expected_closed_swap_info.swap_id = SWAP_ID
    expected_closed_swap_info.amount = 200
    expected_closed_swap_info.state = AtomicSwapInfo.CLOSED
    expected_closed_swap_info.secret_lock = SECRET_LOCK
    expected_closed_swap_info.secret_key = SECRET_KEY
    expected_closed_swap_info.is_initiator = True
    expected_closed_swap_info.sender_address = BOT_ADDRESS
    expected_closed_swap_info.receiver_address = ALICE_ADDRESS
    serialized_expected_closed_swap_info = expected_closed_swap_info.SerializeToString(
    )

    expected_state = {
        ADDRESS_TO_STORE_SWAP_INFO_BY: serialized_expected_closed_swap_info,
        ALICE_ADDRESS: serialized_expected_alice_account,
    }

    AtomicSwapHandler().apply(transaction=transaction_request,
                              context=mock_context)

    state_as_list = mock_context.get_state(
        addresses=[ADDRESS_TO_STORE_SWAP_INFO_BY, ALICE_ADDRESS])
    state_as_dict = {entry.address: entry.data for entry in state_as_list}

    assert expected_state == state_as_dict
示例#10
0
def test_atomic_swap_init():
    """
    Case: initialize swap of bot's Remme node tokens to Alice's ERC20 Remme tokens.
    Expect: bot sends commission to the zero account address, swap amount is decreased from bot account.
    """

    atomic_swap_init_payload = AtomicSwapInitPayload(
        receiver_address=ALICE_ADDRESS,
        sender_address_non_local=BOT_ETHEREUM_ADDRESS,
        amount=TOKENS_AMOUNT_TO_SWAP,
        swap_id=SWAP_ID,
        secret_lock_by_solicitor=BOT_IT_IS_INITIATOR_MARK,
        email_address_encrypted_by_initiator=ALICE_EMAIL_ADDRESS_ENCRYPTED_BY_INITIATOR,
        created_at=CURRENT_TIMESTAMP,
    )

    transaction_payload = TransactionPayload()
    transaction_payload.method = AtomicSwapMethod.INIT
    transaction_payload.data = atomic_swap_init_payload.SerializeToString()

    serialized_transaction_payload = transaction_payload.SerializeToString()

    transaction_header = TransactionHeader(
        signer_public_key=BOT_PUBLIC_KEY,
        family_name=TRANSACTION_REQUEST_ACCOUNT_HANDLER_PARAMS.get('family_name'),
        family_version=TRANSACTION_REQUEST_ACCOUNT_HANDLER_PARAMS.get('family_version'),
        inputs=INPUTS,
        outputs=OUTPUTS,
        dependencies=[],
        payload_sha512=hash512(data=serialized_transaction_payload),
        batcher_public_key=RANDOM_NODE_PUBLIC_KEY,
        nonce=time.time().hex().encode(),
    )

    serialized_header = transaction_header.SerializeToString()

    transaction_request = TpProcessRequest(
        header=transaction_header,
        payload=serialized_transaction_payload,
        signature=create_signer(private_key=BOT_PRIVATE_KEY).sign(serialized_header),
    )

    bot_account = Account()
    bot_account.balance = 5000
    serialized_bot_account = bot_account.SerializeToString()

    zero_account = Account()
    zero_account.balance = 0
    serialized_zero_account = zero_account.SerializeToString()

    swap_commission_setting = Setting()
    swap_commission_setting.entries.add(key=SETTINGS_SWAP_COMMISSION, value=str(SWAP_COMMISSION_AMOUNT))
    serialized_swap_commission_setting = swap_commission_setting.SerializeToString()

    genesis_members_setting = Setting()
    genesis_members_setting.entries.add(key=SETTINGS_KEY_ZERO_ADDRESS_OWNERS, value=f'{BOT_PUBLIC_KEY},')
    serialized_genesis_members_setting = genesis_members_setting.SerializeToString()

    mock_context = StubContext(inputs=INPUTS, outputs=OUTPUTS, initial_state={
        BLOCK_INFO_CONFIG_ADDRESS: SERIALIZED_BLOCK_INFO_CONFIG,
        BLOCK_INFO_ADDRESS: SERIALIZED_BLOCK_INFO,
        BOT_ADDRESS: serialized_bot_account,
        ZERO_ADDRESS: serialized_zero_account,
        ADDRESS_TO_GET_SWAP_COMMISSION_AMOUNT_BY: serialized_swap_commission_setting,
        ADDRESS_TO_GET_GENESIS_MEMBERS_AS_STRING_BY: serialized_genesis_members_setting,
    })

    swap_info = AtomicSwapInfo()
    swap_info.swap_id = SWAP_ID
    swap_info.state = AtomicSwapInfo.OPENED
    swap_info.amount = TOKENS_AMOUNT_TO_SWAP
    swap_info.created_at = CURRENT_TIMESTAMP
    swap_info.email_address_encrypted_optional = ALICE_EMAIL_ADDRESS_ENCRYPTED_BY_INITIATOR
    swap_info.sender_address = BOT_ADDRESS
    swap_info.sender_address_non_local = BOT_ETHEREUM_ADDRESS
    swap_info.receiver_address = ALICE_ADDRESS
    swap_info.is_initiator = True
    serialized_swap_info = swap_info.SerializeToString()

    expected_bot_account = Account()
    expected_bot_account.balance = 5000 - TOKENS_AMOUNT_TO_SWAP - SWAP_COMMISSION_AMOUNT
    serialized_expected_bot_account = expected_bot_account.SerializeToString()

    expected_zero_account = Account()
    expected_zero_account.balance = SWAP_COMMISSION_AMOUNT
    serialized_expected_zero_account = expected_zero_account.SerializeToString()

    expected_state = {
        BOT_ADDRESS: serialized_expected_bot_account,
        ZERO_ADDRESS: serialized_expected_zero_account,
        ADDRESS_TO_STORE_SWAP_INFO_BY: serialized_swap_info,
    }

    AtomicSwapHandler().apply(transaction=transaction_request, context=mock_context)

    state_as_list = mock_context.get_state(addresses=[
        ADDRESS_TO_STORE_SWAP_INFO_BY, BOT_ADDRESS, ZERO_ADDRESS,
    ])
    state_as_dict = {entry.address: entry.data for entry in state_as_list}

    assert expected_state == state_as_dict
示例#11
0
    def get_context(self):
        context = super().get_context()
        context.AMOUNT = 10000
        context.COMMISSION = 100

        context.swap_id = generate_random_key()
        context.swap_address = AtomicSwapHandler.make_address_from_data(context.swap_id)
        context.secret_key = "039eaa877ff63694f8f09c8034403f8b5165a7418812a642396d5d539f90b170"
        context.secret_lock = "b605112c2d7489034bbd7beab083fb65ba02af787786bb5e3d99bb26709f4f68"
        context.now = datetime.datetime.now()
        context.created_at = int(context.now.timestamp())
        context.email_address = ""
        context.sender_address_non_local = ""

        swap_info = AtomicSwapInfo()
        swap_info.swap_id = context.swap_id
        swap_info.state = AtomicSwapInfo.OPENED
        swap_info.is_initiator = False
        swap_info.amount = context.AMOUNT
        swap_info.created_at = context.created_at
        swap_info.email_address_encrypted_optional = context.email_address
        swap_info.sender_address = self.account_address1
        swap_info.sender_address_non_local = context.sender_address_non_local
        swap_info.receiver_address = self.account_address2
        swap_info.secret_lock = context.secret_lock
        context.swap_info = swap_info
        return context
示例#12
0
 def swap_get(self, swap_id):
     atomic_swap_info = AtomicSwapInfo()
     atomic_swap_info.ParseFromString(self.get_value(self.make_address_from_data(swap_id)))
     return atomic_swap_info
示例#13
0
def test_approve_atomic_swap():
    """
    Case: approve atomic swap.
    Expect: atomic swap state is changed to approved.
    """
    atomic_swap_init_payload = AtomicSwapApprovePayload(swap_id=SWAP_ID, )

    transaction_payload = TransactionPayload()
    transaction_payload.method = AtomicSwapMethod.APPROVE
    transaction_payload.data = atomic_swap_init_payload.SerializeToString()

    serialized_transaction_payload = transaction_payload.SerializeToString()

    transaction_header = TransactionHeader(
        signer_public_key=BOT_PUBLIC_KEY,
        family_name=TRANSACTION_REQUEST_ACCOUNT_HANDLER_PARAMS.get(
            'family_name'),
        family_version=TRANSACTION_REQUEST_ACCOUNT_HANDLER_PARAMS.get(
            'family_version'),
        inputs=INPUTS,
        outputs=OUTPUTS,
        dependencies=[],
        payload_sha512=hash512(data=serialized_transaction_payload),
        batcher_public_key=RANDOM_NODE_PUBLIC_KEY,
        nonce=time.time().hex().encode(),
    )

    serialized_header = transaction_header.SerializeToString()

    transaction_request = TpProcessRequest(
        header=transaction_header,
        payload=serialized_transaction_payload,
        signature=create_signer(
            private_key=BOT_PRIVATE_KEY).sign(serialized_header),
    )

    existing_swap_info = AtomicSwapInfo()
    existing_swap_info.swap_id = SWAP_ID
    existing_swap_info.state = AtomicSwapInfo.SECRET_LOCK_PROVIDED
    existing_swap_info.sender_address = BOT_ADDRESS
    existing_swap_info.secret_lock = SECRET_LOCK
    existing_swap_info.is_initiator = True
    serialized_existing_swap_info = existing_swap_info.SerializeToString()

    mock_context = StubContext(inputs=INPUTS,
                               outputs=OUTPUTS,
                               initial_state={
                                   ADDRESS_TO_STORE_SWAP_INFO_BY:
                                   serialized_existing_swap_info,
                               })

    expected_swap_info = AtomicSwapInfo()
    expected_swap_info.swap_id = SWAP_ID
    expected_swap_info.state = AtomicSwapInfo.APPROVED
    expected_swap_info.sender_address = BOT_ADDRESS
    expected_swap_info.secret_lock = SECRET_LOCK
    expected_swap_info.is_initiator = True
    serialized_expected_swap_info = expected_swap_info.SerializeToString()

    expected_state = {
        ADDRESS_TO_STORE_SWAP_INFO_BY: serialized_expected_swap_info,
    }

    AtomicSwapHandler().apply(transaction=transaction_request,
                              context=mock_context)

    state_as_list = mock_context.get_state(
        addresses=[ADDRESS_TO_STORE_SWAP_INFO_BY])
    state_as_dict = {entry.address: entry.data for entry in state_as_list}

    assert expected_state == state_as_dict
示例#14
0
 async def swap_get(self, swap_id):
     atomic_swap_info = AtomicSwapInfo()
     swap_address = self.make_address_from_data(swap_id)
     raw_swap = await self.get_value(swap_address)
     atomic_swap_info.ParseFromString(raw_swap)
     return atomic_swap_info
示例#15
0
def test_atomic_swap_init_already_taken_id():
    """
    Case: initialize swap of bot's Remme node tokens to Alice's ERC20 Remme tokens with already existing swap id.
    Expect: invalid transaction error is raised with atomic swap id has already been taken error message.
    """

    atomic_swap_init_payload = AtomicSwapInitPayload(
        receiver_address=ALICE_ADDRESS,
        sender_address_non_local=BOT_ETHEREUM_ADDRESS,
        amount=TOKENS_AMOUNT_TO_SWAP,
        swap_id=SWAP_ID,
        secret_lock_by_solicitor=BOT_IT_IS_INITIATOR_MARK,
        email_address_encrypted_by_initiator=ALICE_EMAIL_ADDRESS_ENCRYPTED_BY_INITIATOR,
        created_at=CURRENT_TIMESTAMP,
    )

    transaction_payload = TransactionPayload()
    transaction_payload.method = AtomicSwapMethod.INIT
    transaction_payload.data = atomic_swap_init_payload.SerializeToString()

    serialized_transaction_payload = transaction_payload.SerializeToString()

    transaction_header = TransactionHeader(
        signer_public_key=BOT_PUBLIC_KEY,
        family_name=TRANSACTION_REQUEST_ACCOUNT_HANDLER_PARAMS.get('family_name'),
        family_version=TRANSACTION_REQUEST_ACCOUNT_HANDLER_PARAMS.get('family_version'),
        inputs=INPUTS,
        outputs=OUTPUTS,
        dependencies=[],
        payload_sha512=hash512(data=serialized_transaction_payload),
        batcher_public_key=RANDOM_NODE_PUBLIC_KEY,
        nonce=time.time().hex().encode(),
    )

    serialized_header = transaction_header.SerializeToString()

    transaction_request = TpProcessRequest(
        header=transaction_header,
        payload=serialized_transaction_payload,
        signature=create_signer(private_key=BOT_PRIVATE_KEY).sign(serialized_header),
    )

    swap_info = AtomicSwapInfo()
    swap_info.swap_id = SWAP_ID
    swap_info.state = AtomicSwapInfo.OPENED
    swap_info.amount = TOKENS_AMOUNT_TO_SWAP
    swap_info.created_at = CURRENT_TIMESTAMP
    swap_info.email_address_encrypted_optional = ALICE_EMAIL_ADDRESS_ENCRYPTED_BY_INITIATOR
    swap_info.sender_address = BOT_ADDRESS
    swap_info.sender_address_non_local = BOT_ETHEREUM_ADDRESS
    swap_info.receiver_address = ALICE_ADDRESS
    serialized_swap_info = swap_info.SerializeToString()

    mock_context = StubContext(inputs=INPUTS, outputs=OUTPUTS, initial_state={
        ADDRESS_TO_STORE_SWAP_INFO_BY: serialized_swap_info,
    })

    with pytest.raises(InvalidTransaction) as error:
        AtomicSwapHandler().apply(transaction=transaction_request, context=mock_context)

    assert 'Atomic swap ID has already been taken, please use a different one.' == str(error.value)
示例#16
0
def test_expire_atomic_swap_before_invalid_withdrawal_by_bot():
    """
    Case: to expire atomic swap by bot if 48 hasn't been passed from atomic swap initialization timestamp.
    Expect: invalid transaction error is raised with signer is not the one who opened the swap. error message.
    """
    atomic_swap_close_payload = AtomicSwapExpirePayload(swap_id=SWAP_ID, )

    transaction_payload = TransactionPayload()
    transaction_payload.method = AtomicSwapMethod.EXPIRE
    transaction_payload.data = atomic_swap_close_payload.SerializeToString()

    serialized_transaction_payload = transaction_payload.SerializeToString()

    transaction_header = TransactionHeader(
        signer_public_key=BOT_PUBLIC_KEY,
        family_name=TRANSACTION_REQUEST_ACCOUNT_HANDLER_PARAMS.get(
            'family_name'),
        family_version=TRANSACTION_REQUEST_ACCOUNT_HANDLER_PARAMS.get(
            'family_version'),
        inputs=INPUTS,
        outputs=OUTPUTS,
        dependencies=[],
        payload_sha512=hash512(data=serialized_transaction_payload),
        batcher_public_key=RANDOM_NODE_PUBLIC_KEY,
        nonce=time.time().hex().encode(),
    )

    serialized_header = transaction_header.SerializeToString()

    transaction_request = TpProcessRequest(
        header=transaction_header,
        payload=serialized_transaction_payload,
        signature=create_signer(
            private_key=BOT_PRIVATE_KEY).sign(serialized_header),
    )

    existing_swap_info_to_expire = AtomicSwapInfo()
    existing_swap_info_to_expire.swap_id = SWAP_ID
    existing_swap_info_to_expire.state = AtomicSwapInfo.OPENED
    existing_swap_info_to_expire.sender_address = BOT_ADDRESS
    existing_swap_info_to_expire.created_at = CURRENT_TIMESTAMP
    existing_swap_info_to_expire.is_initiator = True
    serialized_existing_swap_info_to_expire = existing_swap_info_to_expire.SerializeToString(
    )

    mock_context = StubContext(inputs=INPUTS,
                               outputs=OUTPUTS,
                               initial_state={
                                   ADDRESS_TO_STORE_SWAP_INFO_BY:
                                   serialized_existing_swap_info_to_expire,
                                   BLOCK_INFO_CONFIG_ADDRESS:
                                   SERIALIZED_BLOCK_INFO_CONFIG,
                                   BLOCK_INFO_ADDRESS: SERIALIZED_BLOCK_INFO,
                               })

    with pytest.raises(InvalidTransaction) as error:
        AtomicSwapHandler().apply(transaction=transaction_request,
                                  context=mock_context)

    assert f'Swap initiator needs to wait 24 hours since timestamp {CURRENT_TIMESTAMP} to withdraw.' == \
           str(error.value)
示例#17
0
    def _swap_init(self, context, signer_pubkey, swap_init_payload):
        """
        if SecretLockOptionalBob is provided, Bob uses _swap_init to respond to requested swap
        Otherwise, Alice uses _swap_init to request a swap and thus, Bob can't receive funds until Alice "approves".
        """
        address_swap_info_is_stored_by = self.make_address_from_data(swap_init_payload.swap_id)
        swap_information = get_data(context, AtomicSwapInfo, address_swap_info_is_stored_by)

        if swap_information:
            raise InvalidTransaction('Atomic swap ID has already been taken, please use a different one.')

        block_info = self._get_latest_block_info(context)
        block_time = block_info.timestamp

        swap_information = AtomicSwapInfo()
        swap_information.swap_id = swap_init_payload.swap_id
        swap_information.state = AtomicSwapInfo.OPENED
        swap_information.amount = swap_init_payload.amount
        swap_information.created_at = block_time
        swap_information.secret_lock = swap_init_payload.secret_lock_by_solicitor
        swap_information.email_address_encrypted_optional = swap_init_payload.email_address_encrypted_by_initiator
        swap_information.sender_address = AccountHandler().make_address_from_data(signer_pubkey)
        swap_information.sender_address_non_local = swap_init_payload.sender_address_non_local
        swap_information.receiver_address = swap_init_payload.receiver_address
        swap_information.is_initiator = not swap_init_payload.secret_lock_by_solicitor

        commission_amount = int(_get_setting_value(context, SETTINGS_SWAP_COMMISSION))
        if commission_amount < 0:
            raise InvalidTransaction('Wrong commission address.')

        swap_total_amount = swap_information.amount + commission_amount

        account = get_data(context, Account, swap_information.sender_address)

        if account is None:
            account = Account()

        if account.balance < swap_total_amount:
            raise InvalidTransaction(
                f'Not enough balance to perform the transaction in the amount (with a commission) {swap_total_amount}.'
            )

        transfer_payload = AccountClient.get_transfer_payload(ZERO_ADDRESS, commission_amount)

        transfer_state = AccountHandler()._transfer_from_address(
            context, swap_information.sender_address, transfer_payload,
        )

        sender_account = transfer_state.get(swap_information.sender_address)
        sender_account.balance -= swap_information.amount

        return {
            address_swap_info_is_stored_by: swap_information,
            **transfer_state,
        }