Звоним бесплатно, используя блокчейн-сервис ENUMER

в 13:47, , рубрики: asterisk, blockchain, cryptocurrencies, enum, IT-стандарты, Анализ и проектирование систем, Блог компании Emercoin, Криптовалюты, Разработка систем связи, связь, телекоммуникации, телефония

В статье рассказывается о децентрализованной реализации протокола IP-телефонии ENUM на базе блокчейна криптовалюты Emercoin. Дано описание ситемы ENUMER, её отличие от других реализаций ENUM. Также приведены инструкции по разворачиванию узла ENUMER и дан пример конфигурации VOIP АТС FreeSWITCH для работы с ENUMER.

Звоним бесплатно, используя блокчейн-сервис ENUMER - 1

Введение

В мире всё большее применение находит IP-телефония, которая обладает рядом преимуществ по сравнению с классической. IP-решения как правило дешевле классических и обладают большей функциональностью. В настоящее время IP ATC находят применение в различных организациях, но основные телефонные сети продолжают использовать классические решения, пусть даже и на базе цифровых АТС. В результате такого положения вещей, связь даже между двумя IP ATC происходит через обычную телефонную сеть, PSTN. Рассмотрим пример:

Пусть у нас есть организация «Покупатель», и другая организация, «Продавец». Обе организации используют для своих внутренних нужд IP ATC (также называемые PBX). Пусть сотрудник Покупателя нашёл в рекламе объявление Продавца, которое его заинтересовало. Покупатель решил позвонить Продавцу, и набирает его номер. Вызов происходит следующим образом:

Звоним бесплатно, используя блокчейн-сервис ENUMER - 2

При этом звонке, происходит соединение двух IP ATC через цепочку PSTN-провайдеров, и соответственно покупатель платит за звонок провайдеру, который перенаправляет звонок из VOIP-сети в PSTN-сеть (приземляет звонок). Ну а далее идут взаимозачёты между PSTN-провайдерами, которые нас не интересуют.

Если бы IP_ATC_покупателя «знала бы дорогу», она могла бы через Интернет напрямую соединиться с IP_ATC_продавца, и тем избавила бы покупателя от платежа PSTN-приземлятелю, и звонок бы вышел бесплатным:

Звоним бесплатно, используя блокчейн-сервис ENUMER - 3

Да и надёжность и качество соединения было бы значительно выше, так как в такой цепочке и меньше промежуточных элементов, и перекодирований голосового трафика.

Понятно, что «знать все дороги», то есть поддерживать всевозможную карту прямых IP-соединений весьма трудоёмко, и был разработан сетевой протокол преобразования номера телефона в «дорогу», то есть в URI, который назвали ENUM.

ENUM

Протокол ENUM (rfc6116) работает как распределённая адресная книга, и позволяет по номеру телефона «найти дорогу» к соответствующей IP ATC, которая обслуживает этот номер. Описание протокола: https://en.wikipedia.org/wiki/Telephone_number_mapping

Идея использования протокола состоит в следующем:

  • Продавец регистрирует свою IP ATC (PBX) в системе ENUM.
  • При звонке IP ATC покупателя перед использования PSTN-сети пытается найти «короткую дорогу», для чего делает запрос в систему ENUM.
  • Если в системе есть «короткий путь» — АТС покупателя использует его. Если же нет – то она использует стандартный путь, через PSTN, и платит за приземление звонка обычным образом.

Выгода от ENUM

Использование ENUM даёт выгоду как для инициатора, так и для получателя звонка. Для них это:

  • Улучшение качества сигнала за счёт прямого цифрового соединения.
  • Повышенная скорость и надёжность соединения.
  • Обход лимита PSTN-оператора на количество линий или использованных минут.
  • Невозможна ситуация отказа в обслуживании по причине перегрузки оператора.
  • Для звонящего – звонок всегда бесплатен (прямая экономия).
  • Для получателя – бесплатен тоже, даже если у него toll-free number, когда он должен платить за входящие (тоже экономия).
  • Использование ENUM происходит автоматически, незаметно для пользователей, и требует только минимального конфигурирования IP ATC.

Протокол ENUM прост и легковесен, технически эффективен, и все наиболее популярные IP ATC его поддерживают. Тем не менее, широкого распространения от не получил, и тому есть ряд причин организационно-экономического характера, ставшими проблемами для широкого внедрения. Рассмотрим их подробнее.

Проблемы текущих реализаций ENUM

  1. Первая проблема ENUM – то, что сейчас просто нет такого места, где бы владелец обычной IP ATC мог бы зарегистрировать свой номер (или несколько номеров) и соответствующие SIP URI. Вернее – такие места были, например, freenum.org или e164.org. Но freenum давно недоступен, а e164.org прекратил работу в ноябре 2016. Да, где-то продолжают какое-то существование ENUM-сервера типа e164.arpa. Но это сервис – не для владельцев IP ATC или частных лиц, а «межпровайдерный узел», где крупные и взаимно проверенные VOIP-провайдеры дают информацию о возможности приземления в их сети. Эдакий «междусобойчик для своих», куда обычному владельцу IP ATC дороги нет.
  2. Вторая проблема – критическая зависимость работы сети от централизованного сервиса, который может вести себя некорректно, или вообще не работать. Наглядный тому пример – e164.org. В последние 2 года – работал нестабильно, и кроме того, занимался непотребством, например – несмотря на зарегистрированные у него toll-free номера, направлял трафик не на IP ATC абонентов, а на «своих приземлятелей», которые естественно не забывали выставлять счета за доставленный по PSTN-сети звонки. Естественно, это поведение приводило к тому, что у владельцев toll-free номеров пропадал экономический стимул регистрировать эти номера в e164.org.
    А примерно за месяц до закрытия – e164.org вообще перестал отдавать ENUM-ответы, несмотря на то, что записи в нём содержались корректные (мы проверяли).
  3. Так как классическая централизованная ENUM-система пользуется транспортом DNS, она уязвима против всевозможных атак на инфраструктуру DNS, таких как DNS-Spoofing, DNS-hijacking, и им подобных. Естественно, сбои в DNS-системе также приводят к отказу в обслуживании и в системе ENUM.
  4. Ну и последнее, но важное: Нагрузка на такой сервер получается весьма большой, ибо запрос делается при попытке каждого звонка с IP ATC. Да, запросы кэшируются промежуточными DNS-серверами, но тем не менее – нагрузка всё равно остаётся высокой для сервера, обслуживающего все телефонные станции всего мира. Как следствие – отказы в обслуживании или задержки с обработкой запросов, а также высокая цена серверной инфраструктуры.

Вышеназванные причины «провала» имеют организационно-технический характер, проистекающий из централизованной организации сервиса и непрозрачности алгоритма разрешения запроса. Действительно, централизованная реализация ENUM, как и любого сетевого сервиса, зависит от работоспособности сайта, который обслуживает создание ENUM-записей и поддержку их актуальными, а также соответствующего DNS-сервера, который разрешает ENUM-запросы. Иными словами, доверие к такой ENUM-системе не может превышать доверия к организации, которая управляет этой системой. Доверие также снижает тот факт, что совершенно неизвестен алгоритм и критерии, по которым тот или иной централизованный сервис разрешает ENUM-запросы. Так, например, сегодня он может работать как надо, а завтра – перенаправлять куда следует звонки на некий номер, интересующий компетентных товарищей (которым дело до всего). Например, на промежуточный VOIP-узел, который в свою очередь может сделать с трафиком всякое. Почему вдруг перенаправлять? Ну может быть, владелец номера играет джаз…

Соответственно, выход из строя такого сервиса приводит к одномоментному останову всей сети. Оба эти недостатка в полной мере продемонстрировал e164.org: сначала перенаправлял toll-free звонки не на зарегистрированные номера, а на своего провайдера-приземлятеля, потом вообще отвечал пустыми ответами, а в завершение эпопеи вышел из бизнеса, со всеми базами.

ENUMER — децентрализованный ENUM

Для решения указанных выше проблем мы разработали децентрализованную систему ENUM на базе блокчейна Emercoin. Проект получил название «ENUMER», произошедшее от слов «ENUM» и «Emer». Кратко говоря: ENUM+EMER=ENUMER.

Криптовалюты являются трендом современной финансовой индустрии, и в настоящее время активно развиваются. Основой же всех криптовалют, включая Emercoin, является блокчейн – распределённая доверенная публичная база данных платёжных операций.

Особенностью Emercoin является NVS (Name-Value Storage) – распределённое доверенное хранилище информации общего назначения. То есть каждый участник сети имеет достоверную локальную копию всего блокчейна, и следовательно – всего содержимого NVS, а доверие к содержимому NVS, также как и всему блокчейну, создаётся посредством консолидированных усилий майнеров доказательства работы POW и доказательства доли POS.

На базе этого хранилища уже созданы и эксплуатируются системы безопасности emcSSH/emcSSL, а также неуязвимая децентрализованная система доменных имён EmerDNS.

Данный проект децентрализованного ENUM основан на системе EmerDNS, и обладает следующими преимуществами перед классической централизованной реализацией ENUM:

  • Высокая скорость разрешения ENUM-запросов в силу локальности их обработки.
  • Высокая надёжность обработки запроса. Даже в случае потери соединения с другими участниками сети, запросы будут продолжать разрешаться через локальную копию блокчейна.
  • Анонимность ENUM-запросов. Так как запросы не покидают локального компьютера или доверенной локальной сети, никто снаружи не может узнать, что происходит и кого вообще ищут.
  • Невозможность атаки на DNS-инфраструктуру вследствие отсутствия таковой [3].
  • В силу истинной децентрализации сети, доверие к сети не зависит от доверия к какой-либо одной компании. Невозможна ситуация, когда вся система будет остановлена по решению кого-либо одного [2]. Сеть ENUM будет продолжать работать, пока сотни независимых майнеров закрывают блоки и подтверждают консенсус блокчейна.
  • Алгоритм обработки ENUM-запроса выполняется на стороне запрашивающего. Программный код, который это делает, доступен на Github, и может быть проанализирован кем угодно на предмет корректности. Невозможна ситуация, когда ENUM-сервер меняет правила по ходу игры. То есть запрашивается собственный локальный сервер посредством локального соединения (или через доверенную сеть).
  • В силу децентрализованности пиринговой сети Emercoin, она масштабируется неограниченно. Следовательно, нет «узкого горла», в котором фокусируются запросы со всего мира. Каждый может поставить себе сколько угодно независимых узлов EmerCoin, и разрешать свои ENUM-запросы на этом кластере. То есть решена проблема [4].
  • ENUM-записи создаются и модифицируются самими же пользователями, через локальный кошелёк EmerCoin. Таким образом, не требуется сервер или другое место [1], куда надо вносить информацию. Каждый пользователь делает это у себя локально, а «место, куда сообщать о себе» — становится просто не нужным. Каждый сообщает своему локальному кошельку, а тот уже транслирует ENUM-запись в пиринговую сеть.

В результате, получаем систему:

  • Быструю
  • Надёжную
  • Защищённую
  • Масштабируемую
  • Не зависящую критически от кого бы то ни было
  • Не подверженную выходу из строя вследствие волюнтаристского решения
  • Без возможности блокировки неугодных
  • С прозрачным алгоритмом разрешения запросов
  • С возможность управления записью через локальный кошелёк, а не «сервер в интернете»

Но при таких впечатляющих преимуществах, децентрализованный ENUM имеет и свои специфические проблемы, связанные с публичной доступностью инфраструктуры. Так, кто угодно может создать запись с любым содержимым, и захватить запись, соответствующую чужому телефонному номеру, и её содержимым либо заблокировать ENUM-доступ для истинного хозяина телефона, либо того хуже – перенаправить звонок куда-либо не туда. Звонишь, например, в прачечную, а попадаешь в министерство культуры…

Для предотвращения подобных злоупотреблений, в системе предусмотрены верификаторы. Это агенты, которые проверяют, что та или иная ENUM-запись действительно принадлежит владельцу соответствующего номера, и после этого передают владельцу ENUM-записи подпись – вердикт того, что запись верная, и ею можно пользоваться. Также с целью пресечения злоупотреблений система ENUMER работает только с индивидуальными телефонными номерами. То есть невозможно одной ENUM-записью отхватить изрядный кусок номерного пространства.

Как создать верифицированную запись в системе ENUMER

Владелец IP ATC, желающий получать звонки с использованием ENUM, должен вначале настроить свою IP ATC таким образом, чтобы иметь возможность получать входящие звонки на SIP URI «из мира». Детали настройки зависят от конкретной IP АТС (PBX) и сетевого окружения, и ничем не отличаются от обычный настройки PBX без блокчейна.

После этого надо разместить в EmerCoin NVS верифицированную ENUM-запись, которой и будут пользоваться звонящие. Для этого надо предпринять следующие действия:

Установить кошелёк Emercoin

Кошелёк (узел) Emercoin является клиентской программой распределённой ENUM-сети (да и не только ENUM), и чем-то вроде «личного кабинета», где пользователь может создать свои ENUM-записи и потом обновлять их по мере необходимости. Кошелёк можно скачать с http://emercoin.com/#download

Для управления записями рекомендуем использовать GUI-кошелёк для Вашей ОС.

После скачивания и запуска, подождите, пока кошелёк не синхронизирует локальную копию блокчейна. Это может занять несколько часов.

Получить монеты EMC

Далее, надо где-то приобрести монет EMC, необходимых для загрузки и обновления записей в NVS. Их можно приобрести на одной из бирж, либо же обратиться к разработчикам.
Для резервирования одной записи на 10 лет и нескольких последующих обновлений требуется примерно 0.05EMC, что по текущему курсу составляет $0.06. В общем, цена вполне приемлема. А если принять во внимание, что в скором времени цена на NVS-запись будет снижена в 100 раз, можно считать расходы на обслуживание ENUMER-записи близкими к нулю.

Адрес, куда получать монеты, находится в меню “File/Receiving Addresses”, и выглядит примерно так: ERFJfQGwmZEomHQHGZsRFLZEyBxaWsCHTo.

Создать ENUM-запись

Для хранения ENUM-записей используется доверенное децентрализованное хранилище — EmerCoin NVS (Name-Value Storage). Доступ к записям NVS делается либо через GUI кошелька в табе “Manage Names”, либо командами JSON API, приведёнными в документации. NVS-запись, как явствует из названия, состоит из Имени (Name) и значения (Value).

Звоним бесплатно, используя блокчейн-сервис ENUMER - 4

Name

Для подсистемы ENUM имена записей имеют следующий вид:

enum:phone_num:N
Здесь:

  • enum — префикс сервиса
  • phone_num — телефонный номер в формате E164
  • N – номер попытки для борьбы со скваттерами (десятичное число)

Например:

enum:18009359935:0

Если про префикс сервиса и номер и так всё понятно, то назначение параметра N нуждается в разъяснении. Это число нужно, чтобы защитить истинного владельца номера от скваттеров, которые могут захватить ENUM-запись, соответствующую чужому номеру, и посредством неё пробовать делать всякие нехорошие вещи. Использовать засквоченную запись они по любому не смогут (см. про верификацию ниже), но при отсутствии номера попытки N у настоящего хозяина номера не будет возможности внести свою запись. В случае же использования номера N реальный хозяин телефонного номера может создать запись с первым свободным значением N. Например предположим, что Вы хотите создать ENUM-запись для телефонного номера 18009359935, а приведённое в примере выше – уже захватил какой-то скваттер. Не беда, создайте имя «enum:18009359935:1», и после верификации система будет верить именно Вашему имени, а скваттеру останется любоваться бесполезной записью, в которой он сжёг свои монеты. Если и это имя засквочено – попробуйте создать запись с номером N=2, и так далее.

Value

Поле Value cостоит из нескольких строк. Строки – либо U-директивы NAPTR (rfc3402) ENUM (начинаются с E2U), либо подписи верификаторов (начинаются с SIG). Одна ENUM-запись может иметь как несколько директив, так и несколько подписей от различных верификаторов. Пример value, включающий U-директирву и подпись:

E2U+sip=100|10|!^(.*)$!sip:17772325555@in.callcentric.com!
SIG=ver:enum|IC00zMELlEwmMHLpQs8=

У Вас пока нет подписей (строки подписей Вас предоставят верификаторы), поэтому при создании записи внесите только U-директивы (хотя бы одну), которые и обеспечат направление звонков на Вашу IP ATC. Структура директив следующая:

Service=Priority|Preference|Regex

В примере выше:

  • Service -> E2U+sip
  • Priority -> 100
  • Preference ->10
  • Regex -> !^(.*)$!sip:17772325555@in.callcentric.com!

Смысл полей и их названия соответствуют стандартам классического ENUM. Для тех, кому лень читать документацию, краткая инструкция для начинающих:

Почти всегда сервис – это как в примере выше, E2U+sip для SIP IP ATC. Возможны другие варианты, например E2U+iax или что-либо подобное. Как упоминалось выше, в ENUM-записи может присутствовать несколько U-директив.

Priority, Preference – оставьте как в примере для начала. Не рекомендуется их сильно уменьшать.

Самое главное – это Regex. Это правило, по которому телефонный номер преобразовывается в SIP URI. Разделителем выступает “!”. В первой части – regex накладывается на телефонный номер, а во второй – этот номер подставляется вместо 1, и тем самым формируется SIP URI.
Ещё хороший пример использования regex: !^+?441865332(.*)$!sip:1@nominet.org.uk!

Провести верификацию

Итак, ENUM запись создана, и блокчейн её принял. Она уже всем доступна. Но IP ATC звонящих её дружно игнорируют, так как в ней нет подписей верификаторов, которым они верят. И это – правильно. Ибо мало ли кто создал запись с Вашим телефонным номером? И что, теперь весь трафик отправлять куда там указано, не глядя? Нет, трафик будут отправлять только на основе верифицированных записей, содержащих подпись верификатора.

В настоящее время ещё не сделана система автоматической верификации, но в планах стоит её изготовление и ввод в эксплуатацию. Если Вы желаете поучаствовать в партнёрстве по созданию и эксплуатации подсистемы автоматической верификации, свяжитесь с нами: enumer@emercoin.com. Покамест же верификация происходит через человека-оператора.

Для получения подписи напишите e-mail на адрес enumer@emercoin.com. В письме укажите NVS-имя Вашей записи ENUMER, например: enum:18009359935:0.

В течение нескольких дней оператор проверит наличие записи, и сделает тестовый звонок на соответствующий номер, чтобы убедиться, что хозяин номера в курсе запроса и согласен использовать ENUMER. После успешной верификации мы отправим ответ, содержащий подпись, которую Вы и внесёте в Вашу NVS-запись. После внесения подписи, Ваша запись станет доверенной, и другие пользователи смогут её использовать для прямых звонков в Ваш PBX.

Добавить подпись верификатора в свою ENUM-запись

Внеся эту подпись в Вашу ENUM-запись и выполнив NAME_UPDATE, Вы доказываете всем владение номером, и звонящие (клиенты ENUMER-а), которые верят данному верификатору, будут отправлять звонки не на PSTN, а на SIP URI Вашей IP ATC.

В ЕNUM-записи подписей может быть несколько, от разных верификаторов. Клиент пользуется ENUM-записью, если там присутствует хотя бы одна подпись от одного из тех верификаторов, которым он верит.

После добавления подписи – Ваша ENUM-запись активна, и клиенты ENUMER-а могут звонить Вам напрямую, не используя PSTN-сети.

Как звонить, используя ENUMER (клиент)

Теперь, после того, как запись о Вашей IP ATC внесена в ENUMER, осталось начать пользоваться системой на благо себя и других абонентов. Для этого надо сделать, чтобы Ваша IP ATC посылала ENUM-запросы в Emercoin NVS.

Использование сервиса enum.enumer.org

Мы создали публичный шлюз в ENUMER, и любой пользователь PBX безо всяких настроек блокчейна и тп может им пользоваться в своей АТС. Для этого надо послать ENUM-запрос в DNS: на резолвер enum.enumer.org. Пример тестового запроса из командной строки и ответа:


$ dig -t naptr +short 53995390081.enum.enumer.org
100 10 "u" "E2U+sip" "!^(.*)$!sip:\1@tollfree.alcazarnetworks.com!" .

Но при использовании нашего шлюза, Вы потенциально получаете все рассмотренные выше проблемы, связанные с централизацией и использованием внешнего сервера. Поэтому основным назначением нашего шлюза является предоставление тестовой среды, и не более того. Мы настоятельно не рекомендуем использовать наш шлюз для практического применения в системе под эксплуатацией, а развернуть собственный шлюз в EmerDNS, как будет рассказано ниже.

Разворачивание шлюза в EmerDNS

Наиболее правильный, быстрый и безопасный вариант – поставить у себя демон-кошелёк EmerCoin, и развернуть шлюз в emcDNS в локальной сети или даже на сервере, где работает IP ATC. Документация по разворачиванию такого шлюза – тут . Смотрите раздел «Integration into a regular DNS tree».

В конфиг BINDa (или другого DNS-proxy) необходимо будет добавить ссылку на пятую зону «enum» по аналогии с четырьмя уже имеющимися от EmerDNS.

Кроме того, в конфиге кошелька emercoin.conf необходимо добавить «$enum» к списку обслуживаемых зон, как в примере ниже. Использование знака «$» вместо точки указывает кошельку, что эта зона будет обслуживаться по правилам ENUM rfc6116. Кроме того, в конфиг кошелька необходимо добавить два параметра, специфичных для ENUM:

  • Список доверенных верификаторов enumtrust (в примере ниже – ver:enum|olegh)
  • Имя файла или ссылка на NVS-запись (ссылка начинается с '@'), содержащая список правил для попытки отработать toll-free звонки через публичных приземлятелей, если в ENUMER-e ничего не было найдено для искомого номера. Это enumtollfree.

    В примере ниже, в качестве такого списка выступает ссылка на NVS-запись enum:tollfree, которая в настоящее время содержит правила для приземления toll-free звонков в США, Францию и Польшу. Мы будем поддерживать актуальность этой записи, и добавлять пути на другие страны по мере появления соответствующих приземлятелей.

Таким образом, в конфигурационный файл emercoin.conf надо добавить следующие строки:


emcdnsallowed=$enum|.coin|.emc|.lib|.bazar # Allowed TLDs, including ENUM
enumtrust=ver:enum|olegh
enumtollfree=@enum:tollfree

Для тестирования настроек, попробуйте вручную послать ENUM-запрос в DNS:

$ dig -t naptr +short 53995390081.enum localhost

Ответ должен быть примерно таким:

100 10 "u" "E2U+sip" "!^(.*)$!sip:\1@tollfree.alcazarnetworks.com!" .

Если не работает что-то – попробуйте послать запрос непосредственно в кошелёк, на порт 5335

$ dig -t naptr +short 53995390081.enum localhost –p 5335

И локализовать место сбоя. Но при правильной настройке – всё работает сразу и без проблем.

Подключение IP ATC

Это проще всего. В dialplan-e IP ATC надо указать, что после разрешения внутренних и корпоративных номеров, но перед переходом к PSTN-провайдерам, делаем запрос в ENUMER. Естественно, перед запросом надо привести искомый номер к формату ITU-T E164.

Ниже приведён пример фрагмента dialplan-a для FreeSWITCH, который сначала приводит номер к формату E164 без лидирующего плюса, а потом делает запрос в ENUMER, добавляя плюс при запросе. Если ENUMER не может помочь – делается запрос в e164.arpa. Ну и если и там ничего нет – то dialplan переходит к PSTN-приземлятелям. Аналогичный фрагмент dialplan-a можно создать для другой IP ATC, поддерживающей ENUM (Asterisk, Yate, etc).

<!-- Normalize numbers to ITU-T E164 without PLUS -->
<extension name="Normalize to ITU-T E164 without PLUS">
  <condition field="destination_number" expression="^(?:+|011|00)(d{7,})$" break="on-true">
    <action application="transfer" data="$1"/>
  </condition>
  <condition field="destination_number" expression="^(d{10})$" break="on-true">
    <action application="transfer" data="1$1"/>
  </condition>
  <condition field="destination_number" expression="^(d{7})$" break="on-true">
     <action application="transfer" data="1${home_code}$1"/>
  </condition>
</extension>

<!-- Lookup E164 with PLUS in the ENUMER and e164.arpa -->
<extension name="Lookup ENUM" continue="true">
  <condition field="destination_number" expression="^(d{7,})$" break="on-true">
    <action application="ring_ready"/>
    <action application="set" data="hangup_after_bridge=true"/>
    <action application="set" data="continue_on_fail=true"/>
    <action application="set" data="effective_caller_id_name=+15553550000"/>
    <action application="set" data="effective_caller_id_number=+15553550000"/>
    <action application="enum" data="+$1 enum"/>
    <action application="bridge" data="${enum_auto_route}"/>
    <action application="enum" data="+$1 e164.arpa"/>
    <action application="bridge" data="${enum_auto_route}"/>
    </condition>
</extension>

Автор: olegarch

Источник


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js