Pull to refresh

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

Reading time7 min
Views4.7K

Предисловие


Да, Вы только вдумайтесь, централизованная криптовалюта на 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); // Возвращаем false когда хэш не проходит проверку и 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 для связи
		$this->db = 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; // Не удалось зарегистрировать кошелек в системе.
	}

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

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

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

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

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

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

Да, подключена. (Давайте сделаем пароль 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.

Спасибо за прочтение! Еще увидимся! Это моя первая статья — жду критики.
Tags:
Hubs:
Total votes 25: ↑10 and ↓15-5
Comments18

Articles