Классическая фраза из фильмов звучит примерно так: «Я люблю запах рефакторинга по утрам», верно? Вот как я это помню.
Недавно я изучал базу кода клиента, и хотя моей задачей было просто убедиться, что код работает так, как задумано, я не мог остановиться, но подумал, что небольшой рефакторинг будет иметь большое значение, чтобы сделать код более кратким и дешевым в использовании. и легче читать.
Ниже представлена (очень) очищенная версия исходного кода.
pragma solidity ^0.4.22; contract Voting { address[] public candidates; mapping(address => uint) public votesReceived; function Voting(address[] candidateNames) public { //330717 gas w/ 5 candidates candidates = candidateNames; } function voteForCandidate(address candidate) public { //21404 gas require(isValidCandidate(candidate)); votesReceived[candidate] += 1; } function totalVotesFor(address candidate) view public returns (uint) { require(isValidCandidate(candidate)); return votesReceived[candidate]; } function isValidCandidate(address candidate) view public returns (bool) { for(uint i = 0; i < candidates.length; i++) { if (candidates[i] == candidate) { return true; } } return false; } }
Первое, что привлекло мое внимание, - это использование устаревшего синтаксиса для функции конструктора. В Solidity функция-конструктор контракта обычно определялась с тем же именем, что и сам контракт. То есть, если контракт назывался Голосование, то функция с именем Голосование будет восприниматься как его конструктор, специальная функция, которая запускается только один раз, во время развертывания, но функция с именем голосование (маленькие заглавные буквы) будет нормальной, общедоступной функцией. Эта синтаксическая конструкция была источником ряда дорогостоящих ошибок, когда развернутые контракты оставались неинициализированными, а чувствительные административные функции оставались открытыми для любого DevOps199… Теперь мы используем функцию с именем Конструктор, его сложнее не заметить.
Затем были структуры данных. Хранение массива с адресами всех допустимых кандидатов в цепочке может быть дорогостоящим. Я имею в виду, что идеальная стоимость - это функция полезности наличия данных, доступных контракту для манипулирования, поэтому нам нужно посмотреть, что с ними делает контракт, прежде чем выносить суждение. Здесь он использовался для очистки ввода с использованием функции «isValidCandidate» и ключевого слова «require». Ой, это определенно дорого.
Написание смарт-контрактов ближе к разработке для встраиваемых устройств, чем к веб-разработке, потому что каждое вычисление рассчитывается и оценивается соответствующим образом с помощью механизма «газа». Это делает циклы и чрезмерную запись и чтение из состояния нецелесообразным, потому что даже вызов функции «просмотра» только для чтения будет стоить определенного количества эфира, если будет вызван из другого смарт-контракта, что хорошо, потому что в этом случае плохой разработчик будет стоит дороже, чем хороший :-)
Зная, что Solidity предоставляет нам бесплатные функции получения для типов данных, определенных как «public», функции «totalVotesFor» и «isValidCandidate» дублируют уже доступные функции, просто вызывая « кандидаты [_candidateAddr] .votes »и« кандидаты [_candidateAddr] .isValid »соответственно, поэтому я решил их удалить.
pragma solidity ^0.4.25; contract RefactoredVoting { struct CandidateStruct { bool isValid; uint64 votes; } mapping(address => CandidateStruct) public candidates; constructor(address[] _candidates) public { //205108 gas w/ 5 candidates for (uint i = 0; i < _candidates.length; i++) { candidates[_candidates[i]].isValid = true; } } function voteForCandidate(address _candidate) public { //5910 gas candidates[_candidate].votes += 1; } }
Проведение простого теста с 5 адресами кандидатов показало хороший прогресс с точки зрения затрат на газ. Развертывание теперь стоит ~ 205 тыс. Газа, по сравнению с ~ 330 тыс., А для функции "voteForCandidate" требуется всего ~ 6 тыс. Газа, по сравнению с ~ 21 тыс. В исходном коде.
Можно уменьшить потребление газа при развертывании, сохранив голоса как uint256 (в Ethereum используются 32-байтовые слова), но функция голосования увеличивает до ~ 20 тыс. Газа, и, честно говоря, я не думаю, что кому-то когда-либо понадобится больше, чем 9223372036854775807 голосов. :-)
Получайте лучшие предложения по программному обеспечению прямо в свой почтовый ящик