Как я писал централизованную криптовалюту на PHP. (Часть 1 — Базовые конспекты + Быстрый старт)

—

от автора

Предисловие

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

Криптовалюта, что из себя представляет?

информация взята из Википедии

Криптовалю́та — разновидность цифровой валюты, учёт внутренних расчётных единиц которой обеспечивает децентрализованная платёжная система (нет внутреннего или внешнего администратора или какого-либо его аналога), работающая в полностью автоматическом режиме.

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

Вкратце криптовалюта работает так:

  1. Пользователь сети, имея баланс, отправляет другому анонимному пользователю транзакцию.
  2. Транзакция поступает в сеть.
  3. Майнеры (или-же пользователи, занимающиеся созданием блоков) решают сгенерированную по специальному алгоритму задачу с условиями (допустим, на наличие предшествующих 10-30 нулей в хеше, или наоборот).
  4. Когда условие (или условия) соблюдаются в хэше, благодаря ему, майнер отправляет в сеть блок, оповещающий о создании нового блока и выполнении транзакции.
  5. Получатель получает средства, а майнер — сумму вознаграждения, в виде комиcсии, просчитываемого при подготовки задачи.

Имеет ли смысл писать централизованную криптовалюту, и к тому же на PHP?

Я считаю, не очень. Но, попробовать еще как стоит.

Учитываем конспекты перед разработкой

Мы будем использовать двойную систему проверки хешей используя Proof-of-Work. Имеет смысл делать проверку хешей на программном и сетевом прикладном уровне. Это связано с процессом оптимизации нагрузки на сервера. Поскольку, когда мы будем обращаться к серверу за проверкой хеша много раз, появится такая нагрузка. Поэтому, предлагаю поступить следующим методом:

  1. На программном майнере локально проверять хеш на удовлетворение условий.
  2. Когда на программном майнере условия совпадут с необходимыми, делаем проверку на прикладном сетевом уровне (на уровне сервера).

Это, по-моему лучшему мнению, оптимальный способ распределения нагрузки. Проще несколько раз проверить на компьютере локально, чем много раз спамить запросами на сервер.

Название — я придумал простое, и возможно, запоминаемое название — FlyCoin с трех-значным кодом валюты FLC.

База данных — я воспользуюсь простым и удобным вариантом, PDO.

Криптокошельки — будут генерироваться используя GUID и mt_rand, а в качестве seed, будет использоваться время POSIX.

А тебе лень делать функцию проверки хеша? — Да, поэтому я решил взять код из одного старого (с открытым исходным кодом) майнера такой (уже умершей) криптовалюты Entropy, моего друга, Никиты.

Не верится, что он создал только одну криптовалюту. А есть что-то другое?

Вообще, все его творения (которые иногда даже я использую), Вы можете найти в его группе ВКонтакте. Надеюсь, и Вам они тоже пригодятся.

Погнали!

Начнем с того, чтобы сделать каркас нашей будущей системы. Начнем, пожалуй с разработки прикладного API для нашей криптовалюты. А каркас, выглядит пока так пусто.

<?php  /* 	* The API for FlyCoin */  function checkHash ($hash, $exp) // Где $hash - там находится наш итоговый хэш, а $exp - сложность. Функция взята из Entropy Miner, и подверглась комментированию. {     $sum = 0; // Контрольная сумма нашего хеша.     $len = strlen ($hash); // Cмотрим длину хеша.      for ($i = 0; $i < $len; ++$i)         $sum += 2 * base_convert ($hash[$i], 16, 10); // Проводим простую операцию для просчета контрольной суммы      return $sum % 10 == 0 && substr ($hash, -$exp) === str_repeat ('0', $exp); // Возвращаем true, если хэш не годен, и true - если хэш годен. }  class Block  { 	public $id; // ID блока 	public $prev_hash; // Предыдущий хэш блока 	public $from; // От кого 	public $to; // Кому 	public $ammount; // Сумма 	public $difficult; // Сложность просчета }  class API  { 	protected $db;  	public function __construct () 	{ 		$dsn = "mysql:host=localhost;dbname=flc"; // DSN для связи 		$dbh = new PDO ($dsn, $username, $password); // Попытка подключиться к базе  		try 		{ 			return true; // Соединение удалось. 		} 		catch (PDOException $exception) 		{ 			return false; // Разрыв соединения. 		} 	}  } 

Вот такой он, каркас нашего чуда. Первым делом мы реализуем регистрацию и генерацию GUID к нашим кошелькам. Я сразу же создал незаполненное определение функции API, а также нашел функцию генерации GUID в Google. Не забываем про seed. Я как раз нашел функцию получения seed, для уникального получения GUID.

        function make_seed () // Взято из онлайн документации по PHP         {   	        list ($usec, $sec) = explode (' ', microtime ());   	        return $sec + $usec * 1000000;         }          mt_srand (make_seed ()); 

Прекрасно. А функция для генерации GUID для кошелька выглядит соответственно вот так (а также сама заглушка):

        public function register () // Пустая функция регистрации 	{  	}  	protected function genGUID4 () // Сила Google 	{ 		return sprintf ('%04x%04x-%04x-%04x-%04x-%04x%04x%04x', mt_rand (0, 0xffff), mt_rand (0, 0xffff), mt_rand (0, 0xffff), mt_rand (0, 0x0fff) | 0x4000, mt_rand (0, 0x3fff) | 0x8000, mt_rand (0, 0xffff), mt_rand (0, 0xffff), mt_rand (0, 0xffff)); // Я даже сам не понял, как это работает. 	} 

Поскольку мы делаем все анонимно, мы не будем хранить IP или E-Mail. Никакого подтверждения. Нам и так будет достаточно. За ~25 минут я написал функцию регистрации. От нас потребуется только один пароль. Функция API вернет нам кошелек.

        public function register ($password) // Где $password - там наш пароль 	{ 		if ($this->db) // Защита от дурака 		{  			$walletId = 'FLC@' . $this->genGUID4 (); // Генерируем наш новый адрес кошелька 			$hash = crc32 ($walletId) . $walletId . hash_hmac ('sha512', str_rot13 ($password), 'FLC'); // Сложные вычисления пароля для хэша. 			$hash = hash_hmac ('sha512', $hash, 'FLC2'); // Сам пароль в виде хэша.  			$newUser = $this->db->prepare (" 					INSERT INTO `wallets`  						(walletId, loginHash, balance) 					VALUES 						(:walletId, :hash, :startBalance) 				"); // Готовим запрос в базу.  			$newUser->bindParam (':walletId', $walletId, PDO::PARAM_STR); // Привязываем кошелек к запросу 			$newUser->bindParam (':hash', $hash, PDO::PARAM_STR); // Привязываем хэш к запросу 			$newUser->bindParam (':startBalance', 0, PDO::PARAM_INT); // Привязываем стартовый баланс (нету никакого стартового баланса, ставим твердую 0!) к балансу  			$newUser->execute (); // Исполняем запрос.  			return ['walletId' => $walletId]; // Даем наш номер кошелька (это логин) 		} 		else 			return false; // Не удалось зарегистрировать кошелек в системе. 	} 

Здесь я кратко описал, что делает та или иная строка.

Справедливое сообщение для справедливых людей и программистов.

Если Вы оставили о себе контакты на сайте (на котором, возможно, эта криптовалюта и расположится), и Вам пишут что-то подобное:

«Привет можешь помочь? Я хранил на твоем блокчейне мани, и вдруг я потерял пароль помоги пожалуйста и будешь хорошим» — смело отклоняйте данное предложение. Суть криптовалюты — это хранение вскопанных чисел в безопасности. Никто так не докажет, что он или она является владельцем счета. Таким образом, любой злоумышленник может даже украсть чужие средства даже без участия самой жертвы! Не важно, хоть и действительно является этот человек владельцем счета, лучше отказывать. Если потерял деньги — значит не правильно хранил данные от счета. Ради дополнительной безопасности, далее мы не будем реализовывать восстановление пароля.

Функция получения хэша, представленную здесь можно представить еще так:
$inline$hash (w, pw) = crc32 (w) .w.hmac (pw)$inline$

Функция работает так:

На вход поступает пароль, проверяем, подключена ли база данных:

Да, подключена. (Давайте сделаем пароль 12345)

Генерируем адрес кошелька, и пишем в $walletId — получаем примерно такое: FLC@c1cbe61d-a19d-4c82-ba17-3a577df0aeb5 (этот GUID был сгенерирован другим сайтом, я потом к нему добавил префикс FLC@).

Просчитываем plain-text хэша. В первом череду мы получим следующее: 0087196442FLC@c1cbe61d-a19d-4c82-ba17-3a577df0aeb59ee77779db5a44ef3f60044fcb0e1170218e642cb9adc0789ddd08871ad95c7e1d3b1391c6c79774d319b5141bd3b13b717c3d389206ede659cb280949feac66 (много букв, не удивляйтесь).
Во втором череду, хэш станет сто-процентным HMAC: 5e43cbb404c81efeb35162e5bba0766f20b340fbabcc66fd5f7a6a1993a8ccd43fc6384ac53f939acca4378681e2bb17912eacf4c65b8b63acda9aa2d15f5646 (опять же, много букв, использую SHA-512 HMAC).

Записываем это все в БД.

В ответ пишем номер кошелька, через который можно авторизоваться.

Нет, не подключена.

В таком случае, ожидать чего-то стоющего не придется, возвращаем FALSE.

Так выглядит замудренная система регистрации. На деле она выглядит ну очень простой. А в плане кода — куда сложнее.

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

От себя.

Звучит, как будто мы делаем не криптовалюту, а систему авторизации между PHP на сервере и PHP используя php.exe. Во второй части мы добавим к нашему «торту» второй коржик, в которой будет видно что-то, связанное с майнингом, блокчейном и транзакциями.

Спустя еще 15-25 минут была готова и функция авторизации. Лицезрейте этот код ниже:

        public function login ($walletId, $password) // Авторизация 	{ 		if ($this->db) // Защита от дурака 		{  			$hash = crc32 ($walletId) . $walletId . hash_hmac ('sha512', str_rot13 ($password), 'FLC'); // Сложные вычисления пароля для хэша. 			$hash = hash_hmac ('sha512', $hash, 'FLC2'); // Сам пароль в виде хэша.  			$newUser = $this->db->prepare (" 					SELECT 						* 					FROM 						`wallets` 					WHERE 						walletId = :walletId 					AND 						loginHash = :hash 					LIMIT 						0, 1 				"); // Готовим запрос в базу.  			$newUser->bindParam (':walletId', $walletId, PDO::PARAM_STR); // Привязываем кошелек к запросу 			$newUser->bindParam (':hash', $hash, PDO::PARAM_STR); // Привязываем хэш к запросу  			$newUser->execute (); // Исполняем запрос. 			$object = $newUser->fetch_object (); // Соблюдаем использование ООП, делаем в виде объекта 			if ($object AND hash_equals ($object->hash, $hash)) // Если есть пользователь, и хэши совпадают. 			{ 				$loginString = new stdClass; // Создаем пустое хранилище stdClass 				$loginString->hash = $hash; // Консервируем в него хэш и номер счета. 				$loginString->wallet = $walletId;  				return ['token' => serialize ($loginString)]; // С этой строчкой делаем что угодно, но не распространяем. 			} 			else 				return false; // Пользователь не существует/неправильный пароль. 		} 		else 			return false; // Не удалось войти 	} 

Этот код — почти что регистрация, но действует в обратном алгоритме. Работа с пользователями подошла к концу.

Постусловие

За время этого занимательного времени прочтения статьи, мы сделали следующее:

  • Вкратце познакомились с криптовалютой, и как она должна строится правильно.
  • Коротко ответили на вопрос «Имеет ли смысл делать централизованную криптовалюту, и к тому же на PHP?»
  • Обсудили конспекты перед выполнением данной задачи
  • Реализовали систему крипто-счетов.

А теперь, Вы можете почитать, что было за жалюзями:

Под занавесом

Эта статья писалась около 4-5 часов (весь закат и вечер). За это время, я по мере написания и разрабатывал, но когда дело доходит написать статью на Хабрахабре, желание у меня, осуществлить мечту не угасает. Действительно, из-за простой концепции ООП, мне было проще использовать именно его, нежели MySQLi, или еще хуже, обычные функции с префиксом mysql_, которые считаются устаревшими, и более не пригодны для разработки на PHP.

Что мы сделаем в следующий раз:

  • Сделаем майнинг.
  • Рассмотрим, как устроены транзакции и блоки.
  • Сделаем транзакции, и дадим смысл пока-что не использованному классу Block.

Спасибо за прочтение! Еще увидимся! Это моя первая статья — жду критики.


ссылка на оригинал статьи https://habr.com/ru/post/481392/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *